commit 46729d116b742e298377c73a88c429c763cf839e Author: johnoly99 Date: Wed Apr 17 13:36:51 2013 -0400 Initial upload Initial uploading of firmware to seemecnc git diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ebd21a --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ac0f16 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +############################################################## +###### Repetier Firmware for the Rostock MAX ###### +############################################################## + + +This firmware is a fork from Repetier (https://github.com/repetier) +It is maintained to work with the SeeMeCNC Rostock MAX + diff --git a/Repetier/Commands.cpp b/Repetier/Commands.cpp new file mode 100644 index 0000000..cf56286 --- /dev/null +++ b/Repetier/Commands.cpp @@ -0,0 +1,1129 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. +*/ + +#include "Reptier.h" +#if EEPROM_MODE != 0 +#include "Eeprom.h" +#endif +#include "pins_arduino.h" +#include "ui.h" + +#include + +const int sensitive_pins[] PROGMEM = SENSITIVE_PINS; // Sensitive pin list for M42 + +void check_periodical() { + if(!execute_periodical) return; + execute_periodical=0; + manage_temperatures(); + if(--counter_250ms==0) { + if(manage_monitor<=1+NUM_EXTRUDER) + write_monitor(); + counter_250ms=5; + } + UI_SLOW; +} + +/** \brief Waits until movement cache is empty. + + Some commands expect no movement, before they can execute. This function + waits, until the steppers are stopped. In the meanwhile it buffers incoming + commands and manages temperatures. +*/ +void wait_until_end_of_move() { + while(lines_count) { + gcode_read_serial(); + check_periodical(); + UI_MEDIUM; + } +} +void printPosition() { + OUT_P_F("X:",printer_state.currentPositionSteps[0]*inv_axis_steps_per_unit[0]*(unit_inches?0.03937:1)); + OUT_P_F(" Y:",printer_state.currentPositionSteps[1]*inv_axis_steps_per_unit[1]*(unit_inches?0.03937:1)); + OUT_P_F(" Z:",printer_state.currentPositionSteps[2]*inv_axis_steps_per_unit[2]*(unit_inches?0.03937:1)); + OUT_P_F_LN(" E:",printer_state.currentPositionSteps[3]*inv_axis_steps_per_unit[3]*(unit_inches?0.03937:1)); +} +void print_temperatures() { + float temp = current_extruder->tempControl.currentTemperatureC; +#if HEATED_BED_SENSOR_TYPE==0 + OUT_P_F("T:",temp); +#else + OUT_P_F("T:",temp); + OUT_P_F(" B:",heated_bed_get_temperature()); +#endif +#ifdef TEMP_PID + OUT_P_I(" @:",(autotuneIndex==255?pwm_pos[current_extruder->id]:pwm_pos[autotuneIndex])); // Show output of autotune when tuning! +#endif +#if NUM_EXTRUDER>1 + for(byte i=0;i500) factor=500; + printer_state.feedrate *= (float)factor/(float)printer_state.feedrateMultiply; + printer_state.feedrateMultiply = factor; + OUT_P_I_LN("SpeedMultiply:",factor); +} +void change_flowate_multiply(int factor) { + if(factor<25) factor=25; + if(factor>200) factor=200; + printer_state.extrudeMultiply = factor; + OUT_P_I_LN("FlowMultiply:",factor); +} +void set_fan_speed(int speed,bool wait) { +#if FAN_PIN>=0 + speed = constrain(speed,0,255); + if(wait) + wait_until_end_of_move(); // use only if neededthis to change the speed exactly at that point, but it may cause blobs if you do! + pwm_pos[NUM_EXTRUDER+2] = speed; +#endif +} +#if DRIVE_SYSTEM==3 +void delta_move_to_top_endstops(float feedrate) { + long up_steps = printer_state.zMaxSteps; + for (byte i=0; i<3; i++) + printer_state.currentPositionSteps[i] = 0; + calculate_delta(printer_state.currentPositionSteps, printer_state.currentDeltaPositionSteps); + move_steps(0,0,printer_state.zMaxSteps*ENDSTOP_Z_BACK_MOVE,0,feedrate, true, true); +} + +void home_axis(bool xaxis,bool yaxis,bool zaxis) { + long steps; + bool homeallaxis = (xaxis && yaxis && zaxis) || (!xaxis && !yaxis && !zaxis); + if (X_MAX_PIN > -1 && Y_MAX_PIN > -1 && Z_MAX_PIN > -1 && MAX_HARDWARE_ENDSTOP_X & MAX_HARDWARE_ENDSTOP_Y && MAX_HARDWARE_ENDSTOP_Z) { + UI_STATUS_UPD(UI_TEXT_HOME_DELTA); + // Homing Z axis means that you must home X and Y + if (homeallaxis || zaxis) { + delta_move_to_top_endstops(homing_feedrate[0]); + move_steps(0,0,axis_steps_per_unit[0]*-ENDSTOP_Z_BACK_MOVE,0,homing_feedrate[0]/ENDSTOP_X_RETEST_REDUCTION_FACTOR, true, false); + delta_move_to_top_endstops(homing_feedrate[0]/ENDSTOP_X_RETEST_REDUCTION_FACTOR); + printer_state.currentPositionSteps[0] = 0; + printer_state.currentPositionSteps[1] = 0; + printer_state.currentPositionSteps[2] = printer_state.zMaxSteps; + calculate_delta(printer_state.currentPositionSteps, printer_state.currentDeltaPositionSteps); + printer_state.maxDeltaPositionSteps = printer_state.currentDeltaPositionSteps[0]; + } else { + if (xaxis) printer_state.destinationSteps[0] = 0; + if (yaxis) printer_state.destinationSteps[1] = 0; + split_delta_move(true,false,false); + } + printer_state.countZSteps = 0; + UI_CLEAR_STATUS + } +} +#else +void home_axis(bool xaxis,bool yaxis,bool zaxis) { + long steps; + if(xaxis) { + if ((MIN_HARDWARE_ENDSTOP_X && X_MIN_PIN > -1 && X_HOME_DIR==-1) || (MAX_HARDWARE_ENDSTOP_X && X_MAX_PIN > -1 && X_HOME_DIR==1)){ + UI_STATUS_UPD(UI_TEXT_HOME_X); + steps = (printer_state.xMaxSteps-printer_state.xMinSteps) * X_HOME_DIR; + printer_state.currentPositionSteps[0] = -steps; + move_steps(2*steps,0,0,0,homing_feedrate[0],true,true); + printer_state.currentPositionSteps[0] = 0; + move_steps(axis_steps_per_unit[0]*-ENDSTOP_X_BACK_MOVE * X_HOME_DIR,0,0,0,homing_feedrate[0]/ENDSTOP_X_RETEST_REDUCTION_FACTOR,true,false); + move_steps(axis_steps_per_unit[0]*2*ENDSTOP_X_BACK_MOVE * X_HOME_DIR,0,0,0,homing_feedrate[0]/ENDSTOP_X_RETEST_REDUCTION_FACTOR,true,true); +#if defined(ENDSTOP_X_BACK_ON_HOME) + if(ENDSTOP_X_BACK_ON_HOME > 0) + move_steps(axis_steps_per_unit[0]*-ENDSTOP_X_BACK_ON_HOME * X_HOME_DIR,0,0,0,homing_feedrate[0],true,false); +#endif + long offX = 0; +#if NUM_EXTRUDER>1 + for(byte i=0;i1 + move_steps((current_extruder->xOffset-offX) * X_HOME_DIR,0,0,0,homing_feedrate[0],true,false); +#endif + } + } + if(yaxis) { + if ((MIN_HARDWARE_ENDSTOP_Y && Y_MIN_PIN > -1 && Y_HOME_DIR==-1) || (MAX_HARDWARE_ENDSTOP_Y && Y_MAX_PIN > -1 && Y_HOME_DIR==1)){ + UI_STATUS_UPD(UI_TEXT_HOME_Y); + steps = (printer_state.yMaxSteps-printer_state.yMinSteps) * Y_HOME_DIR; + printer_state.currentPositionSteps[1] = -steps; + move_steps(0,2*steps,0,0,homing_feedrate[1],true,true); + printer_state.currentPositionSteps[1] = 0; + move_steps(0,axis_steps_per_unit[1]*-ENDSTOP_Y_BACK_MOVE * Y_HOME_DIR,0,0,homing_feedrate[1]/ENDSTOP_X_RETEST_REDUCTION_FACTOR,true,false); + move_steps(0,axis_steps_per_unit[1]*2*ENDSTOP_Y_BACK_MOVE * Y_HOME_DIR,0,0,homing_feedrate[1]/ENDSTOP_X_RETEST_REDUCTION_FACTOR,true,true); +#if defined(ENDSTOP_Y_BACK_ON_HOME) + if(ENDSTOP_Y_BACK_ON_HOME > 0) + move_steps(0,axis_steps_per_unit[1]*-ENDSTOP_Y_BACK_ON_HOME * Y_HOME_DIR,0,0,homing_feedrate[1],true,false); +#endif + long offY = 0; +#if NUM_EXTRUDER>1 + for(byte i=0;i1 + move_steps(0,(current_extruder->yOffset-offY) * Y_HOME_DIR,0,0,homing_feedrate[1],true,false); +#endif + } + } + if(zaxis) { + if ((MIN_HARDWARE_ENDSTOP_Z && Z_MIN_PIN > -1 && Z_HOME_DIR==-1) || (MAX_HARDWARE_ENDSTOP_Z && Z_MAX_PIN > -1 && Z_HOME_DIR==1)){ + UI_STATUS_UPD(UI_TEXT_HOME_Z); + steps = (printer_state.zMaxSteps-printer_state.zMinSteps) * Z_HOME_DIR; + printer_state.currentPositionSteps[2] = -steps; + move_steps(0,0,2*steps,0,homing_feedrate[2],true,true); + printer_state.currentPositionSteps[2] = 0; + move_steps(0,0,axis_steps_per_unit[2]*-ENDSTOP_Z_BACK_MOVE * Z_HOME_DIR,0,homing_feedrate[2]/ENDSTOP_Z_RETEST_REDUCTION_FACTOR,true,false); + move_steps(0,0,axis_steps_per_unit[2]*2*ENDSTOP_Z_BACK_MOVE * Z_HOME_DIR,0,homing_feedrate[2]/ENDSTOP_Z_RETEST_REDUCTION_FACTOR,true,true); +#if defined(ENDSTOP_Z_BACK_ON_HOME) + if(ENDSTOP_Z_BACK_ON_HOME > 0) + move_steps(0,0,axis_steps_per_unit[2]*-ENDSTOP_Z_BACK_ON_HOME * Z_HOME_DIR,0,homing_feedrate[2],true,false); +#endif + printer_state.currentPositionSteps[2] = (Z_HOME_DIR == -1) ? printer_state.zMinSteps : printer_state.zMaxSteps; + } + } + UI_CLEAR_STATUS +} +#endif + +#if STEPPER_CURRENT_CONTROL==CURRENT_CONTROL_DIGIPOT +// Digipot methods for controling current and microstepping + +#if defined(DIGIPOTSS_PIN) && DIGIPOTSS_PIN > -1 +int digitalPotWrite(int address, unsigned int value) // From Arduino DigitalPotControl example +{ + digitalWrite(DIGIPOTSS_PIN,LOW); // take the SS pin low to select the chip + SPI.transfer(address); // send in the address and value via SPI: + SPI.transfer(value); + digitalWrite(DIGIPOTSS_PIN,HIGH); // take the SS pin high to de-select the chip: + //delay(10); +} + +void set_current(uint8_t driver, unsigned int current) +{ + const uint8_t digipot_ch[] = DIGIPOT_CHANNELS; + digitalPotWrite(digipot_ch[driver], current); +} +#endif + +void current_control_init() //Initialize Digipot Motor Current +{ + #if DIGIPOTSS_PIN && DIGIPOTSS_PIN > -1 + const uint8_t digipot_motor_current[] = MOTOR_CURRENT; + + SPI.begin(); + SET_OUTPUT(DIGIPOTSS_PIN); + for(int i=0;i<=4;i++) + //digitalPotWrite(digipot_ch[i], digipot_motor_current[i]); + set_current(i,digipot_motor_current[i]); + #endif +} +#endif + +#if STEPPER_CURRENT_CONTROL==CURRENT_CONTROL_LTC2600 + +void set_current( byte channel, unsigned short level ) +{ + const byte ltc_channels[] = LTC2600_CHANNELS; + if(channel>LTC2600_NUM_CHANNELS) return; + byte address = ltc_channels[channel]; + char i; + + + // NOTE: Do not increase the current endlessly. In case the engine reaches its current saturation, the engine and the driver can heat up and loss power. + // When the saturation is reached, more current causes more heating and more power loss. + // In case of engines with lower quality, the saturation current may be reached before the nominal current. + + // configure the pins + WRITE( LTC2600_CS_PIN, HIGH ); + SET_OUTPUT( LTC2600_CS_PIN ); + WRITE( LTC2600_SCK_PIN, LOW ); + SET_OUTPUT( LTC2600_SCK_PIN ); + WRITE( LTC2600_SDI_PIN, LOW ); + SET_OUTPUT( LTC2600_SDI_PIN ); + + // enable the command interface of the LTC2600 + WRITE( LTC2600_CS_PIN, LOW ); + + // transfer command and address + for( i=7; i>=0; i-- ) { + WRITE( LTC2600_SDI_PIN, address & (0x01 << i)); + WRITE( LTC2600_SCK_PIN, 1 ); + WRITE( LTC2600_SCK_PIN, 0 ); + } + + // transfer the data word + for( i=15; i>=0; i-- ) { + WRITE( LTC2600_SDI_PIN, level & (0x01 << i)); + WRITE( LTC2600_SCK_PIN, 1 ); + WRITE( LTC2600_SCK_PIN, 0 ); + } + + // disable the ommand interface of the LTC2600 - + // this carries out the specified command + WRITE( LTC2600_CS_PIN, HIGH ); + +} // setLTC2600 + +void current_control_init() //Initialize LTC2600 Motor Current +{ + const unsigned int ltc_current[] = MOTOR_CURRENT; + byte i; + for(i=0;i -1 +void microstep_ms(uint8_t driver, int8_t ms1, int8_t ms2) +{ + if(ms1 > -1) switch(driver) + { + case 0: WRITE( X_MS1_PIN,ms1); break; + case 1: WRITE( Y_MS1_PIN,ms1); break; + case 2: WRITE( Z_MS1_PIN,ms1); break; + case 3: WRITE(E0_MS1_PIN,ms1); break; + case 4: WRITE(E1_MS1_PIN,ms1); break; + } + if(ms2 > -1) switch(driver) + { + case 0: WRITE( X_MS2_PIN,ms2); break; + case 1: WRITE( Y_MS2_PIN,ms2); break; + case 2: WRITE( Z_MS2_PIN,ms2); break; + case 3: WRITE(E0_MS2_PIN,ms2); break; + case 4: WRITE(E1_MS2_PIN,ms2); break; + } +} + +void microstep_mode(uint8_t driver, uint8_t stepping_mode) +{ + switch(stepping_mode) + { + case 1: microstep_ms(driver,MICROSTEP1); break; + case 2: microstep_ms(driver,MICROSTEP2); break; + case 4: microstep_ms(driver,MICROSTEP4); break; + case 8: microstep_ms(driver,MICROSTEP8); break; + case 16: microstep_ms(driver,MICROSTEP16); break; + } +} +void microstep_readings() +{ + out.println_P(PSTR("MS1,MS2 Pins")); + out.print_int_P(PSTR("X: "), READ(X_MS1_PIN)); + out.println_int_P(PSTR(","),READ(X_MS2_PIN)); + out.print_int_P(PSTR("Y: "), READ(Y_MS1_PIN)); + out.println_int_P(PSTR(","),READ(Y_MS2_PIN)); + out.print_int_P(PSTR("Z: "), READ(Z_MS1_PIN)); + out.println_int_P(PSTR(","),READ(Z_MS2_PIN)); + out.print_int_P(PSTR("E0: "), READ(E0_MS1_PIN)); + out.println_int_P(PSTR(","),READ(E0_MS2_PIN)); + out.print_int_P(PSTR("E1: "), READ(E1_MS1_PIN)); + out.println_int_P(PSTR(","),READ(E1_MS2_PIN)); +} +#endif + +void microstep_init() +{ +#if defined(X_MS1_PIN) && X_MS1_PIN > -1 + const uint8_t microstep_modes[] = MICROSTEP_MODES; + SET_OUTPUT(X_MS2_PIN); + SET_OUTPUT(Y_MS2_PIN); + SET_OUTPUT(Z_MS2_PIN); + SET_OUTPUT(E0_MS2_PIN); + SET_OUTPUT(E1_MS2_PIN); + for(int i=0;i<=4;i++) microstep_mode(i,microstep_modes[i]); +#endif +} + + + +/** + \brief Execute the command stored in com. +*/ +void process_command(GCode *com,byte bufferedCommand) +{ + unsigned long codenum; //throw away variable +#ifdef INCLUDE_DEBUG_COMMUNICATION + if(DEBUG_COMMUNICATION) { + if(GCODE_HAS_G(com) || (GCODE_HAS_M(com) && com->M!=111)) { + gcode_command_finished(com); // free command cache + previous_millis_cmd = millis(); + return; + } + } +#endif + if(GCODE_HAS_G(com)) + { + switch(com->G) + { + case 0: // G0 -> G1 + case 1: // G1 + if(get_coordinates(com)) // For X Y Z E F +#if DRIVE_SYSTEM == 3 + split_delta_move(ALWAYS_CHECK_ENDSTOPS, true, true); +#else + queue_move(ALWAYS_CHECK_ENDSTOPS,true); +#endif + break; +#if ARC_SUPPORT + case 2: // CW Arc + case 3: // CCW Arc MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: + { + if(!get_coordinates(com)) break; // For X Y Z E F + float offset[2] = {(GCODE_HAS_I(com)?com->I:0),(GCODE_HAS_J(com)?com->J:0)}; + if(unit_inches) { + offset[0]*=25.4; + offset[1]*=25.4; + } + float position[2] = {printer_state.currentPositionSteps[0]*inv_axis_steps_per_unit[0],printer_state.currentPositionSteps[1]*inv_axis_steps_per_unit[1]}; + float target[2] = {printer_state.destinationSteps[0]*inv_axis_steps_per_unit[0],printer_state.destinationSteps[1]*inv_axis_steps_per_unit[1]}; + float r; + if (GCODE_HAS_R(com)) { + /* + We need to calculate the center of the circle that has the designated radius and passes + through both the current position and the target position. This method calculates the following + set of equations where [x,y] is the vector from current to target position, d == magnitude of + that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to + the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the + length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point + [i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc. + + d^2 == x^2 + y^2 + h^2 == r^2 - (d/2)^2 + i == x/2 - y/d*h + j == y/2 + x/d*h + + O <- [i,j] + - | + r - | + - | + - | h + - | + [0,0] -> C -----------------+--------------- T <- [x,y] + | <------ d/2 ---->| + + C - Current position + T - Target position + O - center of circle that pass through both C and T + d - distance from C to T + r - designated radius + h - distance from center of CT to O + + Expanding the equations: + + d -> sqrt(x^2 + y^2) + h -> sqrt(4 * r^2 - x^2 - y^2)/2 + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + + Which can be written: + + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + + Which we for size and speed reasons optimize to: + + h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) + i = (x - (y * h_x2_div_d))/2 + j = (y + (x * h_x2_div_d))/2 + + */ + r = com->R; + if(unit_inches) r*=25.4; + // Calculate the change in position along each selected axis + double x = target[0]-position[0]; + double y = target[1]-position[1]; + + double h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d) + // If r is smaller than d, the arc is now traversing the complex plane beyond the reach of any + // real CNC, and thus - for practical reasons - we will terminate promptly: + if(isnan(h_x2_div_d)) { OUT_P_LN("error: Invalid arc"); break; } + // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below) + if (com->G==3) { h_x2_div_d = -h_x2_div_d; } + + /* The counter clockwise circle lies to the left of the target direction. When offset is positive, + the left hand circle will be generated - when it is negative the right hand circle is generated. + + + T <-- Target position + + ^ + Clockwise circles with this center | Clockwise circles with this center will have + will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! + \ | / + center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative + | + | + + C <-- Current position */ + + + // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!), + // even though it is advised against ever generating such circles in a single line of g-code. By + // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of + // travel and thus we get the unadvisably long arcs as prescribed. + if (r < 0) { + h_x2_div_d = -h_x2_div_d; + r = -r; // Finished with r. Set to positive for mc_arc + } + // Complete the operation by calculating the actual center of the arc + offset[0] = 0.5*(x-(y*h_x2_div_d)); + offset[1] = 0.5*(y+(x*h_x2_div_d)); + + } else { // Offset mode specific computations + r = hypot(offset[0], offset[1]); // Compute arc radius for mc_arc + } + + // Set clockwise/counter-clockwise sign for mc_arc computations + uint8_t isclockwise = com->G == 2; + + // Trace the arc + mc_arc(position, target, offset,r, isclockwise); + + break; + } +#endif + case 4: // G4 dwell + wait_until_end_of_move(); + codenum = 0; + if(GCODE_HAS_P(com)) codenum = com->P; // milliseconds to wait + if(GCODE_HAS_S(com)) codenum = (long)com->S * 1000; // seconds to wait + codenum += millis(); // keep track of when we started waiting + while((unsigned long)(codenum-millis()) < 2000000000 ){ + gcode_read_serial(); + check_periodical(); + } + break; + case 20: // Units to inches + unit_inches = 1; + break; + case 21: // Units to mm + unit_inches = 0; + break; + case 28: {//G28 Home all Axis one at a time + byte home_all_axis = (GCODE_HAS_NO_XYZ(com)); + home_axis(home_all_axis || GCODE_HAS_X(com),home_all_axis || GCODE_HAS_Y(com),home_all_axis || GCODE_HAS_Z(com)); + } + break; + case 90: // G90 + relative_mode = false; + break; + case 91: // G91 + relative_mode = true; + break; + case 92: // G92 + if(GCODE_HAS_X(com)) printer_state.currentPositionSteps[0] = com->X*axis_steps_per_unit[0]*(unit_inches?25.4:1.0)-printer_state.offsetX; + if(GCODE_HAS_Y(com)) printer_state.currentPositionSteps[1] = com->Y*axis_steps_per_unit[1]*(unit_inches?25.4:1.0)-printer_state.offsetY; + if(GCODE_HAS_Z(com)) printer_state.currentPositionSteps[2] = com->Z*axis_steps_per_unit[2]*(unit_inches?25.4:1.0); + if(GCODE_HAS_E(com)) { + printer_state.currentPositionSteps[3] = com->E*axis_steps_per_unit[3]*(unit_inches?25.4:1.0); + } + break; + + } + previous_millis_cmd = millis(); + } + + else if(GCODE_HAS_M(com)) { // Process M Code + + switch( com->M ) { +#if SDSUPPORT + + case 20: // M20 - list SD card + sd.ls(); + break; + case 21: // M21 - init SD card + sd.mount(); + break; + case 22: //M22 - release SD card + sd.unmount(); + break; + case 23: //M23 - Select file + if(GCODE_HAS_STRING(com)) + sd.selectFile(com->text); + break; + case 24: //M24 - Start SD print + sd.startPrint(); + break; + case 25: //M25 - Pause SD print + sd.pausePrint(); + break; + case 26: //M26 - Set SD index + if(GCODE_HAS_S(com)) + sd.setIndex(com->S); + break; + case 27: //M27 - Get SD status + sd.printStatus(); + break; + case 28: //M28 - Start SD write + if(GCODE_HAS_STRING(com)) + sd.startWrite(com->text); + break; + case 29: //M29 - Stop SD write + //processed in write to file routine above + //savetosd = false; + break; + case 30: // M30 filename - Delete file + if(GCODE_HAS_STRING(com)) + sd.deleteFile(com->text); + break; + case 32: // M32 directoryname + if(GCODE_HAS_STRING(com)) + sd.makeDirectory(com->text); + break; +#endif + case 42: //M42 -Change pin status via gcode + if (GCODE_HAS_S(com) && GCODE_HAS_P(com) && com->S>=0 && com->S<=255) { + int pin_number = com->P; + for(byte i = 0; i < (byte)sizeof(sensitive_pins); i++) { + if (pgm_read_byte(&sensitive_pins[i]) == pin_number) + { + pin_number = -1; + break; + } + } + if (pin_number > -1) { + pinMode(pin_number, OUTPUT); + digitalWrite(pin_number, com->S); + analogWrite(pin_number, com->S); + out.print_int_P(PSTR("Set output "),pin_number); + out.println_int_P(PSTR(" to "),(int)com->S); + } + } + break; + case 104: // M104 + if(reportTempsensorError()) break; + previous_millis_cmd = millis(); + if(DEBUG_DRYRUN) break; +#ifdef EXACT_TEMPERATURE_TIMING + wait_until_end_of_move(); +#else + if(GCODE_HAS_P(com)) + wait_until_end_of_move(); +#endif + if (GCODE_HAS_S(com)) { + if(GCODE_HAS_T(com)) + extruder_set_temperature(com->S,com->T); + else + extruder_set_temperature(com->S,current_extruder->id); + } + break; + case 140: // M140 set bed temp + if(reportTempsensorError()) break; + previous_millis_cmd = millis(); + if(DEBUG_DRYRUN) break; + if (GCODE_HAS_S(com)) heated_bed_set_temperature(com->S); + break; + case 105: // M105 get temperature. Always returns the current temperature, doesn't wait until move stopped + print_temperatures(); + break; + case 109: // M109 - Wait for extruder heater to reach target. + { + if(reportTempsensorError()) break; + previous_millis_cmd = millis(); + if(DEBUG_DRYRUN) break; + UI_STATUS_UPD(UI_TEXT_HEATING_EXTRUDER); + wait_until_end_of_move(); + Extruder *actExtruder = current_extruder; + if(GCODE_HAS_T(com) && com->TT]; + if (GCODE_HAS_S(com)) extruder_set_temperature(com->S,actExtruder->id); +#if defined(SKIP_M109_IF_WITHIN) && SKIP_M109_IF_WITHIN>0 + if(abs(actExtruder->tempControl.currentTemperatureC - actExtruder->tempControl.targetTemperatureC)<(SKIP_M109_IF_WITHIN)) break; // Already in range +#endif + bool dir = actExtruder->tempControl.targetTemperature > actExtruder->tempControl.currentTemperature; + codenum = millis(); + unsigned long waituntil = 0; +#if RETRACT_DURING_HEATUP + byte retracted = 0; +#endif + unsigned long cur_time; + do { + cur_time = millis(); + if( (cur_time - codenum) > 1000 ) { //Print Temp Reading every 1 second while heating up. + print_temperatures(); + codenum = cur_time; + } + check_periodical(); + //gcode_read_serial(); +#if RETRACT_DURING_HEATUP + if (actExtruder==current_extruder && actExtruder->waitRetractUnits > 0 && !retracted && dir && actExtruder->tempControl.currentTemperatureC > actExtruder->waitRetractTemperature) { + move_steps(0,0,0,-actExtruder->waitRetractUnits * axis_steps_per_unit[3],actExtruder->maxFeedrate,false,false); + retracted = 1; + } +#endif + if((waituntil==0 && (dir ? actExtruder->tempControl.currentTemperatureC >= actExtruder->tempControl.targetTemperatureC-0.5:actExtruder->tempControl.currentTemperatureC <= actExtruder->tempControl.targetTemperatureC+0.5)) +#ifdef TEMP_HYSTERESIS + || (waituntil!=0 && (abs(actExtruder->tempControl.currentTemperatureC - actExtruder->tempControl.targetTemperatureC))>TEMP_HYSTERESIS) +#endif + ) { + waituntil = cur_time+1000UL*(unsigned long)actExtruder->watchPeriod; // now wait for temp. to stabalize + } + } while(waituntil==0 || (waituntil!=0 && (unsigned long)(waituntil-cur_time)<2000000000UL)); +#if RETRACT_DURING_HEATUP + if (retracted && actExtruder==current_extruder) { + move_steps(0,0,0,actExtruder->waitRetractUnits * axis_steps_per_unit[3],actExtruder->maxFeedrate,false,false); + } +#endif + } + UI_CLEAR_STATUS; + previous_millis_cmd = millis(); + break; + case 190: // M190 - Wait bed for heater to reach target. +#if HAVE_HEATED_BED + if(DEBUG_DRYRUN) break; + UI_STATUS_UPD(UI_TEXT_HEATING_BED); + wait_until_end_of_move(); +#if HAVE_HEATED_BED + if (GCODE_HAS_S(com)) heated_bed_set_temperature(com->S); +#if defined(SKIP_M190_IF_WITHIN) && SKIP_M190_IF_WITHIN>0 + if(abs(heatedBedController.currentTemperatureC-heatedBedController.targetTemperatureC) 1000 ) { //Print Temp Reading every 1 second while heating up. + print_temperatures(); + codenum = millis(); + } + check_periodical(); + } +#endif +#endif + UI_CLEAR_STATUS; + previous_millis_cmd = millis(); + break; +#ifdef TEMP_PID + case 303: { + int temp = 150; + int cont = 0; + if(GCODE_HAS_S(com)) temp = com->S; + if(GCODE_HAS_P(com)) cont = com->P; + if(cont>=NUM_TEMPERATURE_LOOPS) cont = NUM_TEMPERATURE_LOOPS; + autotunePID(temp,cont); + } + break; +#endif +#if FAN_PIN>-1 && FEATURE_FAN_CONTROL + case 106: //M106 Fan On + set_fan_speed(GCODE_HAS_S(com)?com->S:255,GCODE_HAS_P(com)); + break; + case 107: //M107 Fan Off + set_fan_speed(0,GCODE_HAS_P(com)); + break; +#endif + case 80: // M80 - ATX Power On +#if PS_ON_PIN>-1 + wait_until_end_of_move(); + previous_millis_cmd = millis(); + SET_OUTPUT(PS_ON_PIN); //GND + WRITE(PS_ON_PIN, LOW); +#endif + break; + case 81: // M81 - ATX Power Off +#if PS_ON_PIN>-1 + wait_until_end_of_move(); + SET_OUTPUT(PS_ON_PIN); //GND + WRITE(PS_ON_PIN, HIGH); +#endif + break; + case 82: + relative_mode_e = false; + break; + case 83: + relative_mode_e = true; + break; + case 84: + if(GCODE_HAS_S(com)){ stepper_inactive_time = com->S * 1000; } + else{ + wait_until_end_of_move(); + kill(true); + } + break; + case 85: // M85 + if(GCODE_HAS_S(com)) + max_inactive_time = (long)com->S * 1000; + else + max_inactive_time = 0; + break; + case 92: // M92 + if(GCODE_HAS_X(com)) axis_steps_per_unit[0] = com->X; + if(GCODE_HAS_Y(com)) axis_steps_per_unit[1] = com->Y; + if(GCODE_HAS_Z(com)) axis_steps_per_unit[2] = com->Z; + if(GCODE_HAS_E(com)) current_extruder->stepsPerMM = com->E; + update_ramps_parameter(); + break; + case 111: + if(GCODE_HAS_S(com)) debug_level = com->S; + if(DEBUG_DRYRUN) { // simulate movements without printing + extruder_set_temperature(0,0); +#if NUM_EXTRUDER>1 + extruder_set_temperature(0,1); +#endif +#if HEATED_BED_TYPE!=0 + target_bed_raw = 0; +#endif + } + break; + case 115: {// M115 +#if DRIVE_SYSTEM==3 + out.println_P(PSTR("FIRMWARE_NAME:Repetier_" REPETIER_VERSION " FIRMWARE_URL:https://github.com/repetier/Repetier-Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Rostock EXTRUDER_COUNT:" XSTR(NUM_EXTRUDER) " REPETIER_PROTOCOL:2")); +#else +#if DRIVE_SYSTEM==0 + out.println_P(PSTR("FIRMWARE_NAME:Repetier_" REPETIER_VERSION " FIRMWARE_URL:https://github.com/repetier/Repetier-Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:" XSTR(NUM_EXTRUDER) " REPETIER_PROTOCOL:2")); +#else + out.println_P(PSTR("FIRMWARE_NAME:Repetier_" REPETIER_VERSION " FIRMWARE_URL:https://github.com/repetier/Repetier-Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Core_XY EXTRUDER_COUNT:" XSTR(NUM_EXTRUDER) " REPETIER_PROTOCOL:2")); +#endif +#endif +#if EEPROM_MODE!=0 + float dist = printer_state.filamentPrinted*0.001+epr_get_float(EPR_PRINTING_DISTANCE); + OUT_P_FX("Printed filament:",dist,2); + OUT_P_LN(" m"); + bool alloff = true; + for(byte i=0;itargetTemperatureC>15) alloff = false; + + long seconds = (alloff ? 0 : (millis()-printer_state.msecondsPrinting)/1000)+epr_get_long(EPR_PRINTING_TIME); + long tmp = seconds/86400; + seconds-=tmp*86400; + OUT_P_L("Printing time:",tmp); + tmp=seconds/3600; + OUT_P_L(" days ",tmp); + seconds-=tmp*3600; + tmp = seconds/60; + OUT_P_L(" hours ",tmp); + OUT_P_LN(" min"); +#endif + } + break; + case 114: // M114 + printPosition(); + break; + case 117: // M117 message to lcd + if(GCODE_HAS_STRING(com)) { + UI_STATUS_UPD_RAM(com->text); + } + break; + case 119: // M119 + wait_until_end_of_move(); + #if (X_MIN_PIN > -1) && MIN_HARDWARE_ENDSTOP_X + out.print_P(PSTR("x_min:")); + out.print_P((READ(X_MIN_PIN)^ENDSTOP_X_MIN_INVERTING)?PSTR("H "):PSTR("L ")); + #endif + #if (X_MAX_PIN > -1) && MAX_HARDWARE_ENDSTOP_X + out.print_P(PSTR("x_max:")); + out.print_P((READ(X_MAX_PIN)^ENDSTOP_X_MAX_INVERTING)?PSTR("H "):PSTR("L ")); + #endif + #if (Y_MIN_PIN > -1) && MIN_HARDWARE_ENDSTOP_Y + out.print_P(PSTR("y_min:")); + out.print_P((READ(Y_MIN_PIN)^ENDSTOP_Y_MIN_INVERTING)?PSTR("H "):PSTR("L ")); + #endif + #if (Y_MAX_PIN > -1) && MAX_HARDWARE_ENDSTOP_Y + out.print_P(PSTR("y_max:")); + out.print_P((READ(Y_MAX_PIN)^ENDSTOP_Y_MAX_INVERTING)?PSTR("H "):PSTR("L ")); + #endif + #if (Z_MIN_PIN > -1) && MIN_HARDWARE_ENDSTOP_Z + out.print_P(PSTR("z_min:")); + out.print_P((READ(Z_MIN_PIN)^ENDSTOP_Z_MIN_INVERTING)?PSTR("H "):PSTR("L ")); + #endif + #if (Z_MAX_PIN > -1) && MAX_HARDWARE_ENDSTOP_Z + out.print_P(PSTR("z_max:")); + out.print_P((READ(Z_MAX_PIN)^ENDSTOP_Z_MAX_INVERTING)?PSTR("H "):PSTR("L ")); + #endif + out.println(); + break; +#if BEEPER_TYPE>0 + case 120: // Test beeper function + if(GCODE_HAS_S(com) && GCODE_HAS_P(com)) + beep(com->S,com->P); // Beep test + break; +#endif + #ifdef RAMP_ACCELERATION + case 201: // M201 + if(GCODE_HAS_X(com)) axis_steps_per_sqr_second[0] = com->X * axis_steps_per_unit[0]; + if(GCODE_HAS_Y(com)) axis_steps_per_sqr_second[1] = com->Y * axis_steps_per_unit[1]; + if(GCODE_HAS_Z(com)) axis_steps_per_sqr_second[2] = com->Z * axis_steps_per_unit[2]; + if(GCODE_HAS_E(com)) axis_steps_per_sqr_second[3] = com->E * axis_steps_per_unit[3]; + break; + case 202: // M202 + if(GCODE_HAS_X(com)) axis_travel_steps_per_sqr_second[0] = com->X * axis_steps_per_unit[0]; + if(GCODE_HAS_Y(com)) axis_travel_steps_per_sqr_second[1] = com->Y * axis_steps_per_unit[1]; + if(GCODE_HAS_Z(com)) axis_travel_steps_per_sqr_second[2] = com->Z * axis_steps_per_unit[2]; + if(GCODE_HAS_E(com)) axis_travel_steps_per_sqr_second[3] = com->E * axis_steps_per_unit[3]; + break; + #endif + case 203: // M203 Temperature monitor + if(GCODE_HAS_S(com)) { + if(com->SS; + #if HAVE_HEATED_BED + else manage_monitor=NUM_EXTRUDER; // Set 100 to heated bed + #endif + } + break; + case 204: + { + TemperatureController *temp = ¤t_extruder->tempControl; + if(GCODE_HAS_S(com)) { + if(com->S<0) break; + if(com->SS].tempControl; + #if HAVE_HEATED_BED + else temp = &heatedBedController; + #else + else break; + #endif + } + if(GCODE_HAS_X(com)) temp->pidPGain = com->X; + if(GCODE_HAS_Y(com)) temp->pidIGain = com->Y; + if(GCODE_HAS_Z(com)) temp->pidDGain = com->Z; + updateTempControlVars(temp); + } + break; + case 503: + case 205: // M205 Show EEPROM settings +#if EEPROM_MODE!=0 + epr_output_settings(); +#else + OUT_P_LN("Error: No EEPROM support compiled."); +#endif + break; + case 206: // M206 T[type] P[pos] [Sint(long] [Xfloat] Set eeprom value +#if EEPROM_MODE!=0 + epr_update(com); +#else + OUT_P_LN("Error: No EEPROM support compiled."); +#endif + break; + case 207: // M207 X Z + if(GCODE_HAS_X(com)) + printer_state.maxJerk = com->X; + if(GCODE_HAS_Z(com)) + printer_state.maxZJerk = com->Z; + if(GCODE_HAS_E(com)) { + current_extruder->maxStartFeedrate = com->E; + extruder_select(current_extruder->id); + } + out.print_float_P(PSTR("Jerk:"),printer_state.maxJerk); + out.println_float_P(PSTR(" ZJerk:"),printer_state.maxZJerk); + break; + case 220: // M220 S + if(GCODE_HAS_S(com)) + change_feedrate_multiply(com->S); + else + change_feedrate_multiply(100); + break; + case 221: // M221 S + if(GCODE_HAS_S(com)) + change_flowate_multiply(com->S); + else + change_flowate_multiply(100); + break; + #ifdef USE_ADVANCE + case 223: // Extruder interrupt test + if(GCODE_HAS_S(com)) + printer_state.extruderStepsNeeded+=com->S; + break; + case 232: + out.print_int_P(PSTR(" linear steps:"),maxadv2); + #ifdef ENABLE_QUADRATIC_ADVANCE + out.print_int_P(PSTR(" quadratic steps:"),maxadv); + #endif + out.println_float_P(PSTR(", speed="),maxadvspeed); +#ifdef ENABLE_QUADRATIC_ADVANCE + maxadv=0; +#endif + maxadv2=0; + maxadvspeed=0; + break; +#endif +#if USE_OPS==1 + case 231: // M231 S X Y Z F + if(GCODE_HAS_S(com) && com->S>=0 && com->S<3) + printer_state.opsMode = com->S; + if(GCODE_HAS_X(com) && com->X>=0) + printer_state.opsMinDistance = com->X; + if(GCODE_HAS_Y(com) && com->Y>=0) + printer_state.opsRetractDistance = com->Y; + if(GCODE_HAS_Z(com) && com->Z>=-printer_state.opsRetractDistance) + printer_state.opsRetractBacklash = com->Z; + if(GCODE_HAS_F(com) && com->F>=0 && com->F<=100) + printer_state.opsMoveAfter = com->F; + extruder_select(current_extruder->id); + if(printer_state.opsMode==0) { + out.println_P(PSTR("OPS disabled")); + } else { + if(printer_state.opsMode==1) + out.print_P(PSTR("OPS classic mode:")); + else + out.print_P(PSTR("OPS fast mode:")); + + out.print_float_P(PSTR("min distance = "),printer_state.opsMinDistance); + out.print_float_P(PSTR(", retract = "),printer_state.opsRetractDistance); + out.print_float_P(PSTR(", backslash = "),printer_state.opsRetractBacklash); + if(printer_state.opsMode==2) + out.print_float_P(PSTR(", move after = "),printer_state.opsMoveAfter); + out.println(); + update_extruder_flags(); + } +#ifdef DEBUG_OPS + out.println_int_P(PSTR("Ret. steps:"),printer_state.opsRetractSteps); + out.println_int_P(PSTR("PushBack Steps:"),printer_state.opsPushbackSteps); + out.println_int_P(PSTR("Move after steps:"),printer_state.opsMoveAfterSteps); +#endif + break; +#endif +#ifdef USE_ADVANCE + case 233: + if(GCODE_HAS_Y(com)) + current_extruder->advanceL = com->Y; + out.print_float_P(PSTR("linear L:"),current_extruder->advanceL); +#ifdef ENABLE_QUADRATIC_ADVANCE + if(GCODE_HAS_X(com)) + current_extruder->advanceK = com->X; + out.print_float_P(PSTR(" quadratic K:"),current_extruder->advanceK); +#endif + out.println(); + update_extruder_flags(); + break; +#endif + case 400: // Finish all moves + wait_until_end_of_move(); + break; +#if FEATURE_MEMORY_POSITION + case 401: // Memory position + printer_state.memoryX = printer_state.currentPositionSteps[0]; + printer_state.memoryY = printer_state.currentPositionSteps[1]; + printer_state.memoryZ = printer_state.currentPositionSteps[2]; + break; + case 402: // Go to stored position + { + bool all = !(GCODE_HAS_X(com) && GCODE_HAS_Y(com) && GCODE_HAS_Z(com)); + move_steps((all || GCODE_HAS_X(com) ? printer_state.memoryX-printer_state.currentPositionSteps[0] : 0) + ,(all || GCODE_HAS_Y(com) ? printer_state.memoryY-printer_state.currentPositionSteps[1] : 0) + ,(all || GCODE_HAS_Z(com) ? printer_state.memoryZ-printer_state.currentPositionSteps[2] : 0) + ,0,(GCODE_HAS_F(com) ? com->F : printer_state.feedrate),false,ALWAYS_CHECK_ENDSTOPS); + } + break; +#endif + case 908: // Control digital trimpot directly. + { +#if STEPPER_CURRENT_CONTROL != CURRENT_CONTROL_MANUAL + uint8_t channel,current; + if(GCODE_HAS_P(com) && GCODE_HAS_S(com)) + set_current((uint8_t)com->P, (unsigned int)com->S); +#endif + } + break; + case 500: + { + #if EEPROM_MODE!=0 + epr_data_to_eeprom(false); + OUT_P_LN("Configuration stored to EEPROM."); + #else + OUT_P_LN("Error: No EEPROM support compiled."); + #endif + } + break; + case 501: + { + #if EEPROM_MODE!=0 + epr_eeprom_to_data(); + OUT_P_LN("Configuration loaded from EEPROM."); + #else + OUT_P_LN("Error: No EEPROM support compiled."); + #endif + } + break; + case 502: + { + #if EEPROM_MODE!=0 + epr_eeprom_reset(); + OUT_P_LN("Configuration reset to defaults."); + #else + OUT_P_LN("Error: No EEPROM support compiled."); + #endif + } + break; + + case 350: // Set microstepping mode. Warning: Steps per unit remains unchanged. S code sets stepping mode for all drivers. + { + OUT_P_LN("Set Microstepping"); +#if defined(X_MS1_PIN) && X_MS1_PIN > -1 + if(GCODE_HAS_S(com)) for(int i=0;i<=4;i++) microstep_mode(i,com->S); + if(GCODE_HAS_X(com)) microstep_mode(0,(uint8_t)com->X); + if(GCODE_HAS_Y(com)) microstep_mode(1,(uint8_t)com->Y); + if(GCODE_HAS_Z(com)) microstep_mode(2,(uint8_t)com->Z); + if(GCODE_HAS_E(com)) microstep_mode(3,(uint8_t)com->E); + if(GCODE_HAS_P(com)) microstep_mode(4,com->P); // Original B but is not supported here + microstep_readings(); +#endif + } + break; +#ifdef STEP_COUNTER +#if DRIVE_SYSTEM==3 + case 251: + if(GCODE_HAS_S(com)) { + if (com->S == 0) { + printer_state.countZSteps = 0; + out.println_P(PSTR("Measurement reset.")); + } else if (com->S == 1) { + OUT_P_L_LN("Measure/delta (Steps) =",printer_state.countZSteps * inv_axis_steps_per_unit[2]); + OUT_P_L_LN("Measure/delta =",printer_state.countZSteps * inv_axis_steps_per_unit[2]); + } else if (com->S = 2) { + if (printer_state.countZSteps < 0) + printer_state.countZSteps = -printer_state.countZSteps; + printer_state.zMin = 0; + printer_state.zLength = inv_axis_steps_per_unit[2] * printer_state.countZSteps; + printer_state.zMaxSteps = printer_state.countZSteps; + for (byte i=0; i<3; i++) { + printer_state.currentPositionSteps[i] = 0; + } + calculate_delta(printer_state.currentPositionSteps, printer_state.currentDeltaPositionSteps); + OUT_P_LN("Measured origin set. Measurement reset."); + #if EEPROM_MODE!=0 + epr_data_to_eeprom(false); + OUT_P_LN("EEPROM updated"); + #endif + } + } + break; +#endif +#endif + } + } else if(GCODE_HAS_T(com)) { // Process T code + wait_until_end_of_move(); + extruder_select(com->T); + } else{ + if(DEBUG_ERRORS) { + OUT_P("Unknown command:"); + gcode_print_command(com); + out.println(); + } + } + if(bufferedCommand) + gcode_command_finished(com); // free command cache +} + diff --git a/Repetier/Configuration.h b/Repetier/Configuration.h new file mode 100644 index 0000000..cfcb100 --- /dev/null +++ b/Repetier/Configuration.h @@ -0,0 +1,1107 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + +*/ + +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +/* Some words on units: + +From 0.80 onwards the units used are unified for easier configuration, watch out when transfering from older configs! + +Speed is in mm/s +Acceleration in mm/s^2 +Temperature is in degrees celsius + + +########################################################################################## +## IMPORTANT ## +########################################################################################## + +For easy configuration, the default settings enable parameter storage in EEPROM. +This means, after the first upload many variables can only be changed using the special +M commands as described in the documentation. Changing these value sin the configuration.h +has no effect. Parameters overriden by EEPROM settings are calibartion values, extruder +values except thermistor tables and some other parameter likely to change during usage +like advance steps or ops mode. +To override EEPROM settings with config settings, set EEPROM_MODE 0 + +*/ + + +// BASIC SETTINGS: select your board type, thermistor type, axis scaling, and endstop configuration + +//// The following define selects which electronics board you have. Please choose the one that matches your setup +// MEGA/RAMPS up to 1.2 = 3 +// RAMPS 1.3/RAMPS 1.4 = 33 +// Gen6 = 5 +// Gen6 deluxe = 51 +// Sanguinololu up to 1.1 = 6 +// Sanguinololu 1.2 and above = 62 +// Gen7 1.1 till 1.3.x = 7 +// Gen7 1.4.1 and later = 71 +// Teensylu (at90usb) = 8 // requires Teensyduino +// Printrboard (at90usb) = 9 // requires Teensyduino +// Foltyn 3D Master = 12 +// MegaTronics = 70 +// RUMBA = 80 // Get it from reprapdiscount +// Rambo = 301 +// Arduino Due = 401 // This is only experimental + +#define MOTHERBOARD 301 +#include "pins.h" + +// Uncomment the following line if oyu are using arduino compatible firmware made for Arduino version earlier then 1.0 +// If it is incompatible you will get compiler errors about write functions not beeing compatible! +//#define COMPAT_PRE1 + +/* Define the type of axis movements needed for your printer. The typical case +is a full cartesian system where x, y and z moves are handled by seperate motors. + +0 = full cartesian system, xyz have seperate motors. +1 = z axis + xy H-gantry (x_motor = x+y, y_motor = x-y) +2 = z axis + xy H-gantry (x_motor = x+y, y_motor = y-x) +3 = Delta printers (Rostock, Kossel, RostockMax, Cerberus, etc) +Cases 1 and 2 cover all needed xy H gantry systems. If you get results mirrored etc. you can swap motor connections for x and y. If a motor turns in +the wrong direction change INVERT_X_DIR or INVERT_Y_DIR. +*/ +#define DRIVE_SYSTEM 3 + +// ########################################################################################## +// ## Calibration ## +// ########################################################################################## + +/** Drive settings for the Delta printers +*/ +#if DRIVE_SYSTEM==3 + +// Delta drive type: 0 - belts and pulleys Like Rostock/Rostock MAX, 1 - filament drive like Kossel/Cerebrus (using fishing line etc...) + +#define DELTA_DRIVE_TYPE 0 + +#if DELTA_DRIVE_TYPE == 0 +#define BELT_PITCH 2 // Pitch in mm of drive belt. GT2 = 2mm T2.5=2.5 etc... +#define PULLEY_TEETH 20 // how many teeth on the timing pulley +#define PULLEY_CIRCUMFERENCE (BELT_PITCH * PULLEY_TEETH) + + + +#elif DELTA_DRIVE_TYPE == 1 + +#define PULLEY_DIAMETER 10 // pulley diameter in milimeters on stepper motors - THIS IS FOR STRING DRIVEN SETUPS LIKE KOSSEL, CEREBRUS ETC.... +#define PULLEY_CIRCUMFERENCE (PULLEY_DIAMETER * 3.1415927) +#endif + + + +#define STEPS_PER_ROTATION 200 // 200 is common, which are 1.8 degree per step motors, .9 degree motors would be 400 steps per rotation +#define MICRO_STEPS 16 // RAMBo 1.0 and earlier use 8 (1/8 stepping), 1.1 and on uses 16 (1/16 stepping) RAMPS and Pololu drivers are normally 16 + +/*Number of delta moves in each line. Moves that exceed this figure will be split into multiple lines. +Increasing this figure can use a lot of memory since 7 bytes * size of line buffer * MAX_SELTA_SEGMENTS_PER_LINE +will be allocated for the delta buffer. With defaults 7 * 16 * 30 = 3360 bytes. This leaves ~1K free RAM on an Arduino +Mega. +*/ +#define MAX_DELTA_SEGMENTS_PER_LINE 30 + +// Calculations +#define AXIS_STEPS_PER_MM ((float)(MICRO_STEPS * STEPS_PER_ROTATION) / PULLEY_CIRCUMFERENCE) +#define XAXIS_STEPS_PER_MM AXIS_STEPS_PER_MM +#define YAXIS_STEPS_PER_MM AXIS_STEPS_PER_MM +#define ZAXIS_STEPS_PER_MM AXIS_STEPS_PER_MM + +#else +/** Drive settings for printers with cartesian drive systems */ +/** \brief Number of steps for a 1mm move in x direction. +For xy gantry use 2*belt moved! +Overridden if EEPROM activated. */ +#define XAXIS_STEPS_PER_MM 80.00 +/** \brief Number of steps for a 1mm move in y direction. +For xy gantry use 2*belt moved! +Overridden if EEPROM activated.*/ +#define YAXIS_STEPS_PER_MM 80.00 +/** \brief Number of steps for a 1mm move in z direction Overridden if EEPROM activated.*/ +#define ZAXIS_STEPS_PER_MM 80.00 +#endif + +// ########################################################################################## +// ## Extruder configuration ## +// ########################################################################################## + +/** Number of extruders. Maximum 2 extruder. */ +#define NUM_EXTRUDER 1 + +#define EXT0_X_OFFSET 0 +#define EXT0_Y_OFFSET 0 +// for skeinforge 40 and later, steps to pull the plasic 1 mm inside the extruder, not out. Overridden if EEPROM activated. +#define EXT0_STEPS_PER_MM 584 +// What type of sensor is used? +// 1 is 100k thermistor (Epcos B57560G0107F000 - RepRap-Fab.org and many other) +// 2 is 200k thermistor +// 3 is mendel-parts thermistor (EPCOS G550) +// 4 is 10k thermistor +// 5 is userdefined thermistor table 0 +// 6 is userdefined thermistor table 1 +// 7 is userdefined thermistor table 2 +// 50 is userdefined thermistor table 0 for PTC thermistors +// 51 is userdefined thermistor table 0 for PTC thermistors +// 52 is userdefined thermistor table 0 for PTC thermistors +// 97 Generic thermistor table 1 +// 98 Generic thermistor table 2 +// 99 Generic thermistor table 3 +// 100 is AD595 +// 101 is MAX6675 +#define EXT0_TEMPSENSOR_TYPE 97 +// Analog input pin for reading temperatures or pin enabling SS for MAX6675 +#define EXT0_TEMPSENSOR_PIN TEMP_0_PIN +// WHich pin enables the heater +#define EXT0_HEATER_PIN HEATER_0_PIN +#define EXT0_STEP_PIN E0_STEP_PIN +#define EXT0_DIR_PIN E0_DIR_PIN +// set to false/true for normal / inverse direction +#define EXT0_INVERSE true +#define EXT0_ENABLE_PIN E0_ENABLE_PIN +// For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1 +#define EXT0_ENABLE_ON false +// The following speed settings are for skeinforge 40+ where e is the +// length of filament pulled inside the heater. For repsnap or older +// skeinforge use hiher values. +// Overridden if EEPROM activated. +#define EXT0_MAX_FEEDRATE 45 +// Feedrate from halted extruder in mm/s +// Overridden if EEPROM activated. +#define EXT0_MAX_START_FEEDRATE 40 +// Acceleration in mm/s^2 +// Overridden if EEPROM activated. +#define EXT0_MAX_ACCELERATION 6500 +/** Type of heat manager for this extruder. +- 0 = Simply switch on/off if temperature is reached. Works always. +- 1 = PID Temperature control. Is better but needs good PID values. Defaults are a good start for most extruder. + Overridden if EEPROM activated. +*/ +#define EXT0_HEAT_MANAGER 1 +/** Wait x seconds, after reaching target temperature. Only used for M109. Overridden if EEPROM activated. */ +#define EXT0_WATCHPERIOD 1 + +/** \brief The maximum value, I-gain can contribute to the output. + +A good value is slightly higher then the output needed for your temperature. +Values for starts: +130 => PLA for temperatures from 170-180°C +180 => ABS for temperatures around 240°C + +The precise values may differ for different nozzle/resistor combination. + Overridden if EEPROM activated. +*/ +#define EXT0_PID_INTEGRAL_DRIVE_MAX 190 +/** \brief lower value for integral part + +The I state should converge to the exact heater output needed for the target temperature. +To prevent a long deviation from the target zone, this value limits the lower value. +A good start is 30 lower then the optimal value. You need to leave room for cooling. + Overridden if EEPROM activated. +*/ +#define EXT0_PID_INTEGRAL_DRIVE_MIN 60 +/** P-gain. Overridden if EEPROM activated. */ +#define EXT0_PID_P 35.01 +/** I-gain. Overridden if EEPROM activated. +*/ +#define EXT0_PID_I 1.74 +/** Dgain. Overridden if EEPROM activated.*/ +#define EXT0_PID_D 175.75 +// maximum time the heater is can be switched on. Max = 255. Overridden if EEPROM activated. +#define EXT0_PID_MAX 255 +/** \brief Faktor for the advance algorithm. 0 disables the algorithm. Overridden if EEPROM activated. +K is the factor for the quadratic term, which is normally disabled in newer versions. If you want to use +the quadratic factor make sure ENABLE_QUADRATIC_ADVANCE is defined. +L is the linear factor and seems to be working better then the quadratic dependency. +*/ +#define EXT0_ADVANCE_K 0.0f +#define EXT0_ADVANCE_L 0.0f + +/** \brief Temperature to retract filament when extruder is heating up. Overridden if EEPROM activated. +*/ +#define EXT0_WAIT_RETRACT_TEMP 150 +/** \brief Units (mm/inches) to retract filament when extruder is heating up. Overridden if EEPROM activated. Set +to 0 to disable. +*/ +#define EXT0_WAIT_RETRACT_UNITS 0 + +/** You can run any gcode command son extruder deselect/select. Seperate multiple commands with a new line \n. +That way you can execute some mechanical components needed for extruder selection or retract filament or whatever you need. +The codes are only executed for multiple extruder when changing the extruder. */ +#define EXT0_SELECT_COMMANDS "M120 S5 P5\nM117 Extruder 1" +#define EXT0_DESELECT_COMMANDS "" +/** The extruder cooler is a fan to cool the extruder when it is heating. If you turn the etxruder on, the fan goes on. */ +#define EXT0_EXTRUDER_COOLER_PIN 7 +/** PWM speed for the cooler fan. 0=off 255=full speed */ +#define EXT0_EXTRUDER_COOLER_SPEED 255 + + +// =========================== Configuration for second extruder ======================== +#define EXT1_X_OFFSET 0 +#define EXT1_Y_OFFSET 0 +// for skeinforge 40 and later, steps to pull the plasic 1 mm inside the extruder, not out. Overridden if EEPROM activated. +#define EXT1_STEPS_PER_MM 373 +// What type of sensor is used? +// 1 is 100k thermistor (Epcos B57560G0107F000 - RepRap-Fab.org and many other) +// 2 is 200k thermistor +// 3 is mendel-parts thermistor (EPCOS G550) +// 4 is 10k thermistor +// 5 is userdefined thermistor table 0 +// 6 is userdefined thermistor table 1 +// 7 is userdefined thermistor table 2 +// 50 is userdefined thermistor table 0 for PTC thermistors +// 51 is userdefined thermistor table 0 for PTC thermistors +// 52 is userdefined thermistor table 0 for PTC thermistors +// 97 Generic thermistor table 1 +// 98 Generic thermistor table 2 +// 99 Generic thermistor table 3 +// 100 is AD595 +// 101 is MAX6675 +#define EXT1_TEMPSENSOR_TYPE 1 +// Analog input pin for reading temperatures or pin enabling SS for MAX6675 +#define EXT1_TEMPSENSOR_PIN TEMP_1_PIN +// WHich pin enables the heater +#define EXT1_HEATER_PIN HEATER_1_PIN +#define EXT1_STEP_PIN E1_STEP_PIN +#define EXT1_DIR_PIN E1_DIR_PIN +// set to 0/1 for normal / inverse direction +#define EXT1_INVERSE false +#define EXT1_ENABLE_PIN E1_ENABLE_PIN +// For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1 +#define EXT1_ENABLE_ON false +// The following speed settings are for skeinforge 40+ where e is the +// length of filament pulled inside the heater. For repsnap or older +// skeinforge use eiher values. +// Overridden if EEPROM activated. +#define EXT1_MAX_FEEDRATE 50 +// Feedrate from halted extruder in mm/s +// Overridden if EEPROM activated. +#define EXT1_MAX_START_FEEDRATE 12 +// Acceleration in mm/s^2 +// Overridden if EEPROM activated. +#define EXT1_MAX_ACCELERATION 10000 +/** Type of heat manager for this extruder. +- 0 = Simply switch on/off if temperature is reached. Works always. +- 1 = PID Temperature control. Is better but needs good PID values. Defaults are a good start for most extruder. + Overridden if EEPROM activated. +*/ +#define EXT1_HEAT_MANAGER 1 +/** Wait x seconds, after reaching target temperature. Only used for M109. Overridden if EEPROM activated. */ +#define EXT1_WATCHPERIOD 1 + +/** \brief The maximum value, I-gain can contribute to the output. + +A good value is slightly higher then the output needed for your temperature. +Values for starts: +130 => PLA for temperatures from 170-180°C +180 => ABS for temperatures around 240°C + +The precise values may differ for different nozzle/resistor combination. + Overridden if EEPROM activated. +*/ +#define EXT1_PID_INTEGRAL_DRIVE_MAX 130 +/** \brief lower value for integral part + +The I state should converge to the exact heater output needed for the target temperature. +To prevent a long deviation from the target zone, this value limits the lower value. +A good start is 30 lower then the optimal value. You need to leave room for cooling. + Overridden if EEPROM activated. +*/ +#define EXT1_PID_INTEGRAL_DRIVE_MIN 60 +/** P-gain. Overridden if EEPROM activated. */ +#define EXT1_PID_P 24 +/** I-gain. Overridden if EEPROM activated. +*/ +#define EXT1_PID_I 0.88 +/** Dgain. Overridden if EEPROM activated.*/ +#define EXT1_PID_D 200 +// maximum time the heater is can be switched on. Max = 255. Overridden if EEPROM activated. +#define EXT1_PID_MAX 255 +/** \brief Faktor for the advance algorithm. 0 disables the algorithm. Overridden if EEPROM activated. +K is the factor for the quadratic term, which is normally disabled in newer versions. If you want to use +the quadratic factor make sure ENABLE_QUADRATIC_ADVANCE is defined. +L is the linear factor and seems to be working better then the quadratic dependency. +*/ +#define EXT1_ADVANCE_K 0.0f +#define EXT1_ADVANCE_L 0.0f + +#define EXT1_WAIT_RETRACT_TEMP 150 +#define EXT1_WAIT_RETRACT_UNITS 40 +#define EXT1_SELECT_COMMANDS "M120 S5 P15\nM117 Extruder 2" +#define EXT1_DESELECT_COMMANDS "" +/** The extruder cooler is a fan to cool the extruder when it is heating. If you turn the etxruder on, the fan goes on. */ +#define EXT1_EXTRUDER_COOLER_PIN -1 +/** PWM speed for the cooler fan. 0=off 255=full speed */ +#define EXT1_EXTRUDER_COOLER_SPEED 255 + +/** If enabled you can select the distance your filament gets retracted during a +M140 command, after a given temperature is reached. */ +#define RETRACT_DURING_HEATUP true + +/** PID control only works target temperature +/- PID_CONTROL_RANGE. +If you get much overshoot at the first temperature set, because the heater is going full power to long, you +need to increase this value. For one 6.8 Ohm heater 10 is ok. With two 6.8 Ohm heater use 15. +*/ +#define PID_CONTROL_RANGE 15 + +/** Skip wait, if the extruder temperature is already within x degrees. Only fixed numbers, 0 = off */ +#define SKIP_M109_IF_WITHIN 2 + +/** \brief Set PID scaling + +PID values assume a usable range from 0-255. This can be further limited to EXT0_PID_MAX by to methods. +Set the value to 0: Normal computation, just clip output to EXT0_PID_MAX if computed value is too high. +Set value to 1: Scale PID by EXT0_PID_MAX/256 and then clip to EXT0_PID_MAX. +If your EXT0_PID_MAX is low, you should prefer the second method. +*/ +#define SCALE_PID_TO_MAX 0 + +/** Temperature range for target temperature to hold in M109 command. 5 means +/-5°C + +Uncomment define to use force the temperature into the range for given watchperiod. +*/ +//#define TEMP_HYSTERESIS 5 + +/** Userdefined thermistor table + +There are many different thermistors, which can be combined with different resistors. This result +in unpredictable number of tables. As a resolution, the user can define one table here, that can +be used as type 5 for thermister type in extruder/heated bed definition. Make sure, the number of entries +matches the value in NUM_TEMPS_USERTHERMISTOR0. If you span definition over multiple lines, make sure to end +each line, except the last, with a backslash. The table format is {{adc1,temp1},{adc2,temp2}...} with +increasing adc values. For more informations, read +http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html + +If you have a sprinter temperature table, you have to multiply the first value with 4 and the second with 8. +This firmware works with increased precision, so the value reads go from 0 to 4095 and the temperature is +temperature*8. + +If you have a PTC thermistor instead of a NTC thermistor, keep the adc values increasing and use themistor types 50-52 instead of 5-7! +*/ +/** Number of entries in the user thermistortable 0. Set to 0 to disable it. */ +#define NUM_TEMPS_USERTHERMISTOR0 28 +#define USER_THERMISTORTABLE0 {\ + {1*4,864*8},{21*4,300*8},{25*4,290*8},{29*4,280*8},{33*4,270*8},{39*4,260*8},{46*4,250*8},{54*4,240*8},{64*4,230*8},{75*4,220*8},\ + {90*4,210*8},{107*4,200*8},{128*4,190*8},{154*4,180*8},{184*4,170*8},{221*4,160*8},{265*4,150*8},{316*4,140*8},{375*4,130*8},\ + {441*4,120*8},{513*4,110*8},{588*4,100*8},{734*4,80*8},{856*4,60*8},{938*4,40*8},{986*4,20*8},{1008*4,0*8},{1018*4,-20*8} } + +/** Number of entries in the user thermistortable 1. Set to 0 to disable it. */ +#define NUM_TEMPS_USERTHERMISTOR1 0 +#define USER_THERMISTORTABLE1 {} +/** Number of entries in the user thermistortable 2. Set to 0 to disable it. */ +#define NUM_TEMPS_USERTHERMISTOR2 0 +#define USER_THERMISTORTABLE2 {} + +/** If defined, creates a thermistortable at startup. + +If you dont feel like computing the table on your own, you can use this generic method. It is +a simple approximation which may be not as accurate as a good table computed from the reference +values in the datasheet. You can increase precision if you use a temperature/resistance for +R0/T0, which is near your operating temperature. This will reduce precision for lower temperatures, +which are not realy important. The resistors must fit the following schematic: +@code +VREF ---- R2 ---+--- Termistor ---+-- GND + | | + +------ R1 -------+ + | | + +---- Capacitor --+ + | + V measured +@endcode + +If you don't have R1, set it to 0. +The capacitor is for reducing noise from long thermistor cable. If you don't have have one, it's OK. + +If you don't need the generic table, uncomment the following define. +*/ +#define USE_GENERIC_THERMISTORTABLE_1 + +/* Some examples for different thermistors: + +EPCOS B57560G104+ : R0 = 100000 T0 = 25 Beta = 4036 +EPCOS 100K Thermistor (B57560G1104F) : R0 = 100000 T0 = 25 Beta = 4092 +ATC Semitec 104GT-2 : R0 = 100000 T0 = 25 Beta = 4267 +Honeywell 100K Thermistor (135-104LAG-J01) : R0 = 100000 T0 = 25 Beta = 3974 + +*/ + +/** Reference resistance */ +#define GENERIC_THERM1_R0 100000 +/** Temperature at reference resistance */ +#define GENERIC_THERM1_T0 25 +/** Beta value of thermistor + +You can use the beta from the datasheet or compute it yourself. See +http://reprap.org/wiki/MeasuringThermistorBeta +for more details. +*/ +#define GENERIC_THERM1_BETA 4450 //default was 4267 +/** Start temperature for generated thermistor table */ +#define GENERIC_THERM1_MIN_TEMP -20 +/** End Temperature for generated thermistor table */ +#define GENERIC_THERM1_MAX_TEMP 300 +#define GENERIC_THERM1_R1 0 +#define GENERIC_THERM1_R2 4700 + +// The same for table 2 and 3 if needed + +//#define USE_GENERIC_THERMISTORTABLE_2 +#define GENERIC_THERM2_R0 1042.7 +#define GENERIC_THERM2_T0 170 +#define GENERIC_THERM2_BETA 4036 +#define GENERIC_THERM2_MIN_TEMP -20 +#define GENERIC_THERM2_MAX_TEMP 300 +#define GENERIC_THERM2_R1 0 +#define GENERIC_THERM2_R2 4700 + +//#define USE_GENERIC_THERMISTORTABLE_3 +#define GENERIC_THERM3_R0 1042.7 +#define GENERIC_THERM3_T0 170 +#define GENERIC_THERM3_BETA 4036 +#define GENERIC_THERM3_MIN_TEMP -20 +#define GENERIC_THERM3_MAX_TEMP 300 +#define GENERIC_THERM3_R1 0 +#define GENERIC_THERM3_R2 4700 + +/** Supply voltage to ADC, can be changed be setting ANALOG_REF below to different value. */ +#define GENERIC_THERM_VREF 5 +/** Number of entries in generated table. One entry takes 4 bytes. Higher number of entries increase computation time too. +Value is used for all generic tables created. */ +#define GENERIC_THERM_NUM_ENTRIES 33 + +// uncomment the following line for MAX6675 support. +//#define SUPPORT_MAX6675 + +// ############# Heated bed configuration ######################## + +/** \brief Set true if you have a heated bed conected to your board, false if not */ +#define HAVE_HEATED_BED true + +#define HEATED_BED_MAX_TEMP 125 +/** Skip M190 wait, if heated bed is already within x degrees. Fixed numbers only, 0 = off. */ +#define SKIP_M190_IF_WITHIN 5 + +// Select type of your heated bed. It's the same as for EXT0_TEMPSENSOR_TYPE +// set to 0 if you don't have a heated bed +#define HEATED_BED_SENSOR_TYPE 97 +/** Analog pin of analog sensor to read temperature of heated bed. */ +#define HEATED_BED_SENSOR_PIN TEMP_BED_PIN +/** \brief Pin to enable heater for bed. */ +#define HEATED_BED_HEATER_PIN HEATER_BED_PIN +// How often the temperature of the heated bed is set (msec) +#define HEATED_BED_SET_INTERVAL 5000 + +/** +Heat manager for heated bed: +0 = Bang Bang, fast update +1 = PID controlled +2 = Bang Bang, limited check every HEATED_BED_SET_INTERVAL. Use this with relais driven beds to save life +*/ +#define HEATED_BED_HEAT_MANAGER 1 +/** \brief The maximum value, I-gain can contribute to the output. + +A good value is slightly higher then the output needed for your temperature. +Values for starts: +130 => PLA for temperatures from 170-180°C +180 => ABS for temperatures around 240°C + +The precise values may differ for different nozzle/resistor combination. + Overridden if EEPROM activated. +*/ +#define HEATED_BED_PID_INTEGRAL_DRIVE_MAX 255 +/** \brief lower value for integral part + +The I state should converge to the exact heater output needed for the target temperature. +To prevent a long deviation from the target zone, this value limits the lower value. +A good start is 30 lower then the optimal value. You need to leave room for cooling. + Overridden if EEPROM activated. +*/ +#define HEATED_BED_PID_INTEGRAL_DRIVE_MIN 80 +/** P-gain. Overridden if EEPROM activated. */ +#define HEATED_BED_PID_PGAIN 15 +/** I-gain Overridden if EEPROM activated.*/ +#define HEATED_BED_PID_IGAIN 0.9 +/** Dgain. Overridden if EEPROM activated.*/ +#define HEATED_BED_PID_DGAIN 40 +// maximum time the heater is can be switched on. Max = 255. Overridden if EEPROM activated. +#define HEATED_BED_PID_MAX 255 + +/** Include PID control for all heaters. */ +#define TEMP_PID true + +//// Experimental watchdog and minimal temp +// The watchdog waits for the watchperiod in milliseconds whenever an M104 or M109 increases the target temperature +// If the temperature has not increased at the end of that period, the target temperature is set to zero. It can be reset with another M104/M109 +//#define WATCHPERIOD 5000 //5 seconds + +//// The minimal temperature defines the temperature below which the heater will not be enabled +#define MINTEMP 5 + +//// Experimental max temp +// When temperature exceeds max temp, your heater will be switched off. +// This feature exists to protect your hotend from overheating accidentally, but *NOT* from thermistor short/failure! +// You should use MINTEMP for thermistor short/failure protection. +#define MAXTEMP 260 + +/** \brief Used reference, normally ANALOG_REF_AVCC or ANALOG_REF_AREF for experts ANALOG_REF_INT_2_56 = 2.56V and ANALOG_REF_INT_1_1=1.1V inernaly generated */ +#define ANALOG_REF ANALOG_REF_AVCC + + +// ########################################################################################## +// ## Endstop configuration ## +// ########################################################################################## + +/* By default all endstops are pulled up to high. You need a pullup if you +use a mechanical endstop connected with gnd. Set value to false for no pullup +on this endstop. +*/ +#define ENDSTOP_PULLUP_X_MIN true +#define ENDSTOP_PULLUP_Y_MIN true +#define ENDSTOP_PULLUP_Z_MIN true +#define ENDSTOP_PULLUP_X_MAX true +#define ENDSTOP_PULLUP_Y_MAX true +#define ENDSTOP_PULLUP_Z_MAX true + +//set to true to invert the logic of the endstops +#define ENDSTOP_X_MIN_INVERTING true +#define ENDSTOP_Y_MIN_INVERTING true +#define ENDSTOP_Z_MIN_INVERTING true +#define ENDSTOP_X_MAX_INVERTING false +#define ENDSTOP_Y_MAX_INVERTING false +#define ENDSTOP_Z_MAX_INVERTING false + +// Set the values true where you have a hardware endstop. The Pin numbe ris taken from pins.h. + +#define MIN_HARDWARE_ENDSTOP_X false +#define MIN_HARDWARE_ENDSTOP_Y false +#define MIN_HARDWARE_ENDSTOP_Z false +#define MAX_HARDWARE_ENDSTOP_X true +#define MAX_HARDWARE_ENDSTOP_Y true +#define MAX_HARDWARE_ENDSTOP_Z true + +//If your axes are only moving in one direction, make sure the endstops are connected properly. +//If your axes move in one direction ONLY when the endstops are triggered, set ENDSTOPS_INVERTING to true here + + + +//// ADVANCED SETTINGS - to tweak parameters + +// For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1 +#define X_ENABLE_ON 0 +#define Y_ENABLE_ON 0 +#define Z_ENABLE_ON 0 + +// Disables axis when it's not being used. +#define DISABLE_X false +#define DISABLE_Y false +#define DISABLE_Z false +#define DISABLE_E false + +// Inverting axis direction +#define INVERT_X_DIR false +#define INVERT_Y_DIR false +#define INVERT_Z_DIR false + +//// ENDSTOP SETTINGS: +// Sets direction of endstops when homing; 1=MAX, -1=MIN +#define X_HOME_DIR 1 +#define Y_HOME_DIR 1 +#define Z_HOME_DIR 1 + +// Delta robot radius endstop +#define max_software_endstop_r true + +//If true, axis won't move to coordinates less than zero. +#define min_software_endstop_x false +#define min_software_endstop_y false +#define min_software_endstop_z false + +//If true, axis won't move to coordinates greater than the defined lengths below. +#define max_software_endstop_x true +#define max_software_endstop_y true +#define max_software_endstop_z true + +// If during homing the endstop is reached, ho many mm should the printer move back for the second try +#define ENDSTOP_X_BACK_MOVE 10 +#define ENDSTOP_Y_BACK_MOVE 10 +#define ENDSTOP_Z_BACK_MOVE 10 + +// For higher precision you can reduce the speed for the second test on the endstop +// during homing operation. The homing speed is divided by the value. 1 = same speed, 2 = half speed +#define ENDSTOP_X_RETEST_REDUCTION_FACTOR 4 +#define ENDSTOP_Y_RETEST_REDUCTION_FACTOR 4 +#define ENDSTOP_Z_RETEST_REDUCTION_FACTOR 4 + +// When you have several endstops in one circuit you need to disable it after homing by moving a +// small amount back. This is also the case with H-belt systems. +#define ENDSTOP_X_BACK_ON_HOME 0.5 +#define ENDSTOP_Y_BACK_ON_HOME 3.5 +#define ENDSTOP_Z_BACK_ON_HOME 0 + +// You can disable endstop checking for print moves. This is needed, if you get sometimes +// false signals from your endstops. If your endstops don't give false signals, you +// can set it on for safety. +#define ALWAYS_CHECK_ENDSTOPS true + +// maximum positions in mm - only fixed numbers! +// For delta robot Z_MAX_LENGTH is maximum travel of the towers and should be set to the distance between the hotend +// and the platform when the printer is at its home position. +// If EEPROM is enabled these values will be overidden with the values in the EEPROM +#define X_MAX_LENGTH 354.3 +#define Y_MAX_LENGTH 354.3 +#define Z_MAX_LENGTH 354.3 + +// Coordinates for the minimum axis. Can also be negative if you want to have the bed start at 0 and the printer can go to the left side +// of the bed. Maximum coordinate is given by adding the above X_MAX_LENGTH values. +#define X_MIN_POS 0 +#define Y_MIN_POS 0 +#define Z_MIN_POS 0 + +// ########################################################################################## +// ## Movement settings ## +// ########################################################################################## + +// Microstep setting (Only functional when stepper driver microstep pins are connected to MCU. Currently only works for RAMBO boards +#define MICROSTEP_MODES {8,8,8,8,8} // [1,2,4,8,16] + +// Motor Current setting (Only functional when motor driver current ref pins are connected to a digital trimpot on supported boards) +#define MOTOR_CURRENT {195,195,195,195,0} // Values 0-255 (RAMBO 135 = ~0.75A, 185 = ~1A) +//#define MOTOR_CURRENT {35713,35713,35713,35713,35713} // Values 0-65535 (3D Master 35713 = ~1A) + +// Delta settings +#if DRIVE_SYSTEM==3 +/** \brief Delta rod length +*/ +#define DELTA_DIAGONAL_ROD 269.0 // mm + +/** \brief Number of segments to generate for delta conversions per second of move +*/ +#define DELTA_SEGMENTS_PER_SECOND_PRINT 200 // Move accurate setting for print moves +#define DELTA_SEGMENTS_PER_SECOND_MOVE 200 // Less accurate setting for other moves + +/** \brief Horizontal offset of the universal joints on the end effector (moving platform). +*/ +#define END_EFFECTOR_HORIZONTAL_OFFSET 33 + +/** \brief Horizontal offset of the universal joints on the vertical carriages. +*/ +#define CARRIAGE_HORIZONTAL_OFFSET 35 + +/** \brief Printer radius in mm, measured from the center of the print area to the vertical smooth rod. +*/ + +#define PRINTER_RADIUS 198.25 + +/** \brief Horizontal distance bridged by the diagonal push rod when the end effector is in the center. It is pretty close to 50% of the push rod length (250 mm). +*/ +#define DELTA_RADIUS (PRINTER_RADIUS-END_EFFECTOR_HORIZONTAL_OFFSET-CARRIAGE_HORIZONTAL_OFFSET+0.0) + +/** \brief Enable counter to count steps for Z max calculations +*/ +#define STEP_COUNTER + +/** \brief Experimental calibration utility for delta printers +*/ +#define SOFTWARE_LEVELING + +#endif + +/** After x seconds of inactivity, the stepper motors are disabled. + Set to 0 to leave them enabled. + This helps cooling the Stepper motors between two print jobs. + Overridden if EEPROM activated. +*/ +#define STEPPER_INACTIVE_TIME 600 +/** After x seconds of inactivity, the system will go down as far it can. + It will at least disable all stepper motors and heaters. If the board has + a power pin, it will be disabled, too. + Set value to 0 for disabled. + Overridden if EEPROM activated. +*/ +#define MAX_INACTIVE_TIME 900 +/** Maximum feedrate, the system allows. Higher feedrates are reduced to these values. + The axis order in all axis related arrays is X, Y, Z + Overridden if EEPROM activated. + */ +#define MAX_FEEDRATE_X 200 +#define MAX_FEEDRATE_Y 200 +#define MAX_FEEDRATE_Z 200 + +/** Speed in mm/min for finding the home position. Overridden if EEPROM activated. */ +#define HOMING_FEEDRATE_X 60 +#define HOMING_FEEDRATE_Y 60 +#define HOMING_FEEDRATE_Z 60 + +/* If you have a backlash in both z-directions, you can use this. For most printer, the bed will be pushed down by it's +own weight, so this is nearly never needed. */ +#define ENABLE_BACKLASH_COMPENSATION false +#define Z_BACKLASH 0 +#define X_BACKLASH 0 +#define Y_BACKLASH 0 + +/** Comment this to disable ramp acceleration */ +#define RAMP_ACCELERATION 1 + +/** If your stepper needs a longer high signal then given, you can add a delay here. +The delay is realized as a simple loop wasting time, which is not available for other +computations. So make it as low as possible. For the most common drivers no delay is needed, as the +included delay is already enough. +*/ +#define STEPPER_HIGH_DELAY 0 + +/** The firmware can only handle 16000Hz interrupt frequency cleanly. If you need higher speeds +a faster solution is needed, and this is to double/quadruple the steps in one interrupt call. +This is like reducing your 1/16th microstepping to 1/8 or 1/4. It is much cheaper then 1 or 3 +additional stepper interrupts with all it's overhead. As a result you can go as high as +40000Hz. +*/ +#define STEP_DOUBLER_FREQUENCY 12000 +/** If you need frequencies off more then 30000 you definitely need to enable this. If you have only 1/8 stepping +enabling this may cause to stall your moves when 20000Hz is reached. +*/ +#define ALLOW_QUADSTEPPING true +/** If you reach STEP_DOUBLER_FREQUENCY the firmware will do 2 or 4 steps with nearly no delay. That can be too fast +for some printers causing an early stall. + +*/ +#define DOUBLE_STEP_DELAY 1 // time in us + +/** The firmware supports trajectory smoothing. To acieve this, it divides the stepsize by 2, resulting in +the double computation cost. For slow movements this is not an issue, but for really fast moves this is +too much. The value specified here is the number of clock cycles between a step on the driving axis. +If the interval at full speed is below this value, smoothing is disabled for that line.*/ +#define MAX_HALFSTEP_INTERVAL 1999 + + + + +//###################################################################### +//## Acceleration and Jerk settings ## +//###################################################################### + +// X, Y, Z max acceleration in mm/s^2 for printing moves or retracts. Overridden if EEPROM activated. + +#define MAX_ACCELERATION_UNITS_PER_SQ_SECOND_X 1500 +#define MAX_ACCELERATION_UNITS_PER_SQ_SECOND_Y 1500 +#define MAX_ACCELERATION_UNITS_PER_SQ_SECOND_Z 1500 + +// X, Y, Z max acceleration in mm/s^2 for travel (non-printing) moves. Overridden if EEPROM activated. +#define MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_X 1500 +#define MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_Y 1500 +#define MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_Z 1500 + + +// Make the Jerk Settings identical for Delta printers +#define MAX_JERK 9.0 +#define MAX_ZJERK 9.0 + + + + +/** \brief Number of moves we can cache in advance. + +This number of moves can be cached in advance. If you wan't to cache more, increase this. Especially on +many very short moves the cache may go empty. The minimum value is 5. +*/ +#define MOVE_CACHE_SIZE 16 + +/** \brief Low filled cache size. + +If the cache contains less then MOVE_CACHE_LOW segments, the time per segment is limited to LOW_TICKS_PER_MOVE clock cycles. +If a move would be shorter, the feedrate will be reduced. This should prevent buffer underflows. Set this to 0 if you +don't care about empty buffers during print. +*/ +#define MOVE_CACHE_LOW 10 +/** \brief Cycles per move, if move cache is low. + +This value must be high enough, that the buffer has time to fill up. The problem only occurs at the beginning of a print or +if you are printing many very short segments at high speed. Higher delays here allow higher values in PATH_PLANNER_CHECK_SEGMENTS. +*/ +#define LOW_TICKS_PER_MOVE 250000 + + + + + +// ########################################################################################## +// ## Extruder control ## +// ########################################################################################## + +/** \brief Prescale factor, timer0 runs at. + +All known arduino boards use 64. This value is needed for the extruder timing. */ +#define TIMER0_PRESCALE 64 + +/* Minimum temperature for extruder operation + +This is a saftey value. If your extruder temperature is below this temperature, no +extruder steps are executed. This is to prevent your extruder to move unless the fiament +is at least molten. After having some complains that the extruder does not work, I leave +it 0 as default. +*/ + +#define MIN_EXTRUDER_TEMP 0 + + +/* Activate ooze prevention system + +The ooze prevention system tries to prevent ooze, by a fast retract of the filament every time +printing stops. Most slicing software have already an option to do this. Using OPS_MODE=1 will +in fact mimic this. This works good, but can increase printing time. To reduce the additional +waiting time, the OPS has a fast mode, which performs the retraction during the travelling move. +The only reason, your slicer doesn't do it, is because it can't tell. There is simple no +G-Code command telling the firmware to do that. + +You can always compile including OPS. Then you can disable/enable it anytime you want. To disable it +set USE_OPS 0 + +Caution: Don't enable anti-ooze in your slicer if you are using this. +*/ +#define USE_OPS 1 + +/** \brief Sets the ops operation mode + +0: Off +1: Classic mode. Stop head, retract move to target, push filament back. +2: Fast mode. Retract during move, start pushing back the filament during move. For safty, we start + at with a low speed and wait for the push back, before the pintmove starts. Normally there is some + time needed to wait for the filament. + + Overridden if EEPROM activated. +*/ +#define OPS_MODE 0 + +/** \brief Minimum distance for retraction. + +If a travel move is shorter than this distance, no retraction will occur. This is to prevent +retraction with infill, where the angle to the perimeter needs a short stop. Unit is mm. + Overridden if EEPROM activated. +*/ +#define OPS_MIN_DISTANCE 5.0 + +/** \brief Move printhead only after x% of retract distance have been retracted. + + Overridden if EEPROM activated.*/ +#define OPS_MOVE_AFTER 50.0 +/** \brief Retraction distance in mm. If you want to enable OPS only sometimes, compile with +OPS support and set retraction distance to 0. If you set it to e.g. 3 in your eeprom settings it is enabled. + Overridden if EEPROM activated.*/ +#define OPS_RETRACT_DISTANCE 4.5 + +/** \brief Backslash produced by extruder reversal + +If you are using a bowden extruder, you may need some extra distance to push the filament back into the +original place. This is the value you enter here. Unit is mm. + Overridden if EEPROM activated. +*/ +#define OPS_RETRACT_BACKLASH 0.0 + +/** \brief Enable advance algorithm. + +Without a correct adjusted advance algorithm, you get blobs at points, where acceleration changes. The +effect increases with speed and acceleration difference. Using the advance method decreases this effect. +For more informations, read the wiki. +*/ +#define USE_ADVANCE + +/** \brief enables quadratic component. + +Uncomment to allow a quadratic advance dependency. Linear is the dominant value, so no real need +to activate the quadratic term. Only adds lots of computations and storage usage. */ +//#define ENABLE_QUADRATIC_ADVANCE + + +// ########################################################################################## +// ## Communication configuration ## +// ########################################################################################## + +// AD595 THERMOCOUPLE SUPPORT UNTESTED... USE WITH CAUTION!!!! + +/** \brief Communication speed. + +- 250000 : Fastes with errorrate of 0% with 16 or 32 MHz - update wiring_serial.c in your board files. See boards/readme.txt +- 115200 : Fast, but may produce communication errors on quite regular basis, Error rate -3,5% +- 76800 : Best setting for Arduino with 16 MHz, Error rate 0,2% page 198 AVR1284 Manual. Result: Faster communication then 115200 +- 57600 : Should produce nearly no errors, on my gen 6 it's faster than 115200 because there are no errors slowing down the connection +- 38600 + + Overridden if EEPROM activated. +*/ +//#define BAUDRATE 76800 +//#define BAUDRATE 57600 +//#define BAUDRATE 115200 //uncomment this line for Linux and comment out the 250000 line +#define BAUDRATE 250000 + +/** +Some boards like Gen7 have a power on pin, to enable the atx power supply. If this is defined, +the power will be turned on without the need to call M80 if initially started. +*/ +#define ENABLE_POWER_ON_STARTUP + +/** What shall the printer do, when it receives an M112 emergency stop signal? + 0 = Disable heaters/motors, wait for ever until someone presses reset. + 1 = restart by resetting the AVR controller. The USB connection will not reset if managed by a different chip! +*/ +#define KILL_METHOD 1 + +/** \brief Cache size for incoming commands. + +There should be no reason to increase this cache. Commands are nearly immediately send to +execution. +*/ +#define GCODE_BUFFER_SIZE 2 +/** Appends the linenumber after ever ok send, to acknowledge the received command. Uncomment for plain ok ACK if your host has problems with this */ +#define ACK_WITH_LINENUMBER +/** Communication errors can swollow part of the ok, which tells the host software to send +the next command. Not receiving it will cause your printer to stop. Sending this string every +second, if our queue is empty should prevent this. Uncomment if you don't wan't this feature. */ +#define WAITING_IDENTIFIER "wait" + +/** \brief Sets time for echo debug + +You can set M111 1 which enables ECHO of commands send. This define specifies the position, +when it will be executed. In the original FiveD software, echo is done after receiving the +command. With checksum you know, how it looks from the sending string. With this define +uncommented, you will see the last command executed. To be more specific: It is written after +execution. This helps tracking errors, because there may be 8 or more commands in the queue +and it is elsewise difficult to know, what your reprap is currently doing. +*/ +#define ECHO_ON_EXECUTE + + + +/* ######################################################################################## + ## EEPROM and SD Card SETTINGS ## + ######################################################################################## +Set the EEPROM_MODE to 0 if you always wan't to use the settings in this configuration file. If not, +set it to 1. If you later want to overwrite your current +eeprom settings with configuration defaults, just change EEPROM_MODE to 0 and upload, then power cycle your board. + +IMPORTANT: With mode 1 some changes in configuration.h are not set any more, as they are + taken from the EEPROM. If you have changed your firmware, and uploaded it, and the + values havn't changed, it's prob. because you have eeprom mode 1 selected. +*/ +#define EEPROM_MODE 1 + + +// SD Card Settings +#define SDSUPPORT true // Set to false to disable SD support +#ifndef SDSUPPORT // Some boards have sd support on board. These define the values already in pins.h +#define SDSUPPORT false +#define SD_ALLOW_LONG_NAMES false // If set to false all files with longer names then 8.3 or having a tilde in the name will be hidden +#define SDCARDDETECT 81 // Uncomment to enable or changed card detection pin. With card detection the card is mounted on insertion +#define SDCARDDETECTINVERTED false // Change to true if you get a inserted message on removal. +#endif + +/** Show extended directory including file length. Don't use this with pronterface! */ +#define SD_EXTENDED_DIR +// If you want support for G2/G3 arc commands set to true, otherwise false. +#define ARC_SUPPORT true + +/** You can store the current position with M401 and go back to it with M402. + This works only if feature is set to true. */ +#define FEATURE_MEMORY_POSITION true + +/** Should support for fan control be compiled in. If you enable this make sure +the FAN pin is not the same as for your second extruder. RAMPS e.g. has FAN_PIN in 9 which +is also used for the heater if you have 2 extruders connected. */ +#define FEATURE_FAN_CONTROL true + +/** For displays and keys there are too many permutations to handle them all in once. +For the most common available combinations you can set the controller type here, so +you don't need to configure uicong.h at all. Controller settings > 1 disable usage +of uiconfig.h + +0 = no display +1 = Manual definition of display and keys parameter in uiconfig.h + +The following settings override uiconfig.h! +2 = Smartcontroller on a RAMPS from reprapdiscount +3 = Adafruit RGB controller +4 = Foltyn 3DMaster with display attached +*/ +#define FEATURE_CONTROLLER 1 + +/** +Select the language to use. +0 = english +1 = german +2 = dutch +*/ +#define UI_LANGUAGE 0 + +// This is line 2 of the status display at startup +#define UI_VERSION_STRING2 "Rostock MAX" + +/** How many ms should a single page be shown, until it is switched to the next one.*/ +#define UI_PAGES_DURATION 4000 + +/** Uncomment if you don't want automatic page switching. You can still switch the +info pages with next/previous button/click-encoder */ +#define UI_DISABLE_AUTO_PAGESWITCH true + +/** Time to return to info menu if x millisconds no key was pressed. Set to 0 to disable it. */ +#define UI_AUTORETURN_TO_MENU_AFTER 30000 + +#define FEATURE_UI_KEYS 0 + +/* Normally cou want a next/previous actions with every click of your encoder. +Unfotunately, the encoder have a different count of phase changes between clicks. +Select an encoder speed from 0 = fastest to 2 = slowest that results in one menu move per click. +*/ +#define UI_ENCODER_SPEED 1 +/** \brief bounce time of keys in milliseconds */ +#define UI_KEY_BOUNCETIME 10 + +/** \brief First time in ms until repeat of action. */ +#define UI_KEY_FIRST_REPEAT 500 +/** \brief Reduction of repeat time until next execution. */ +#define UI_KEY_REDUCE_REPEAT 50 +/** \brief Lowest repeat time. */ +#define UI_KEY_MIN_REPEAT 50 + +#define FEATURE_BEEPER true +/** +Beeper sound definitions for short beeps during key actions +and longer beeps for important actions. +Parameter is is delay in microseconds and the secons is the number of repetitions. +Values must be in range 1..255 +*/ +#define BEEPER_SHORT_SEQUENCE 2,2 +#define BEEPER_LONG_SEQUENCE 8,8 + +// ############################################################################### +// ## Values for menu settings ## +// ############################################################################### + +// Values used for preheat +#define UI_SET_PRESET_HEATED_BED_TEMP_PLA 50 +#define UI_SET_PRESET_EXTRUDER_TEMP_PLA 160 +#define UI_SET_PRESET_HEATED_BED_TEMP_ABS 80 +#define UI_SET_PRESET_EXTRUDER_TEMP_ABS 200 +// Extreme values +#define UI_SET_MIN_HEATED_BED_TEMP 35 +#define UI_SET_MAX_HEATED_BED_TEMP 125 +#define UI_SET_MIN_EXTRUDER_TEMP 150 +#define UI_SET_MAX_EXTRUDER_TEMP 260 +#define UI_SET_EXTRUDER_FEEDRATE 3 // mm/sec +#define UI_SET_EXTRUDER_RETRACT_DISTANCE 3 // mm + +#endif + diff --git a/Repetier/Eeprom.cpp b/Repetier/Eeprom.cpp new file mode 100644 index 0000000..9d2238d --- /dev/null +++ b/Repetier/Eeprom.cpp @@ -0,0 +1,726 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. + + Functions in this file are used to communicate using ascii or repetier protocol. +*/ + +#include "Reptier.h" + +#if EEPROM_MODE!=0 +#include "Eeprom.h" +extern void epr_eeprom_to_data(); + +byte epr_compute_checksum() { + int i; + byte checksum=0; + for(i=0;i<2048;i++) { + if(i==EEPROM_OFFSET+EPR_INTEGRITY_BYTE) continue; + checksum += eeprom_read_byte ((unsigned char *)(i)); + } + return checksum; +} + + + +inline void epr_set_byte(uint pos,byte value) { + eeprom_write_byte((unsigned char *)(EEPROM_OFFSET+pos), value); +} +inline void epr_set_int(uint pos,int value) { + eeprom_write_word((unsigned int*)(EEPROM_OFFSET+pos),value); +} +inline void epr_set_long(uint pos,long value) { + eeprom_write_dword((unsigned long*)(EEPROM_OFFSET+pos),value); +} +inline void epr_set_float(uint pos,float value) { + eeprom_write_block(&value,(void*)(EEPROM_OFFSET+pos), 4); +} +void epr_out_prefix(uint pos) { + if(posT) { + case 0: + if(GCODE_HAS_S(com)) epr_set_byte(com->P,(byte)com->S); + break; + case 1: + if(GCODE_HAS_S(com)) epr_set_int(com->P,(int)com->S); + break; + case 2: + if(GCODE_HAS_S(com)) epr_set_long(com->P,(long)com->S); + break; + case 3: + if(GCODE_HAS_X(com)) epr_set_float(com->P,com->X); + break; + } + byte newcheck = epr_compute_checksum(); + if(newcheck!=epr_get_byte(EPR_INTEGRITY_BYTE)) + epr_set_byte(EPR_INTEGRITY_BYTE,newcheck); + epr_eeprom_to_data(); +} + +/** \brief Copy data from EEPROM to variables. +*/ +void epr_eeprom_reset() { + byte version = EEPROM_PROTOCOL_VERSION; + baudrate = BAUDRATE; + max_inactive_time = MAX_INACTIVE_TIME*1000L; + stepper_inactive_time = STEPPER_INACTIVE_TIME*1000L; + axis_steps_per_unit[0] = XAXIS_STEPS_PER_MM; + axis_steps_per_unit[1] = YAXIS_STEPS_PER_MM; + axis_steps_per_unit[2] = ZAXIS_STEPS_PER_MM; + axis_steps_per_unit[3] = 1; + max_feedrate[0] = MAX_FEEDRATE_X; + max_feedrate[1] = MAX_FEEDRATE_Y; + max_feedrate[2] = MAX_FEEDRATE_Z; + homing_feedrate[0] = HOMING_FEEDRATE_X; + homing_feedrate[1] = HOMING_FEEDRATE_Y; + homing_feedrate[2] = HOMING_FEEDRATE_Z; + printer_state.maxJerk = MAX_JERK; + printer_state.maxZJerk = MAX_ZJERK; +#ifdef RAMP_ACCELERATION + max_acceleration_units_per_sq_second[0] = MAX_ACCELERATION_UNITS_PER_SQ_SECOND_X; + max_acceleration_units_per_sq_second[1] = MAX_ACCELERATION_UNITS_PER_SQ_SECOND_Y; + max_acceleration_units_per_sq_second[2] = MAX_ACCELERATION_UNITS_PER_SQ_SECOND_Z; + max_travel_acceleration_units_per_sq_second[0] = MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_X; + max_travel_acceleration_units_per_sq_second[1] = MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_X; + max_travel_acceleration_units_per_sq_second[2] = MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_X; +#endif +#if USE_OPS==1 + printer_state.opsMode = OPS_MODE; + printer_state.opsMinDistance = OPS_MIN_DISTANCE; + printer_state.opsRetractDistance = OPS_RETRACT_DISTANCE; + printer_state.opsRetractBacklash = OPS_RETRACT_BACKLASH; + printer_state.opsMoveAfter = 0; +#endif +#if HAVE_HEATED_BED + heatedBedController.heatManager= HEATED_BED_HEAT_MANAGER; +#ifdef TEMP_PID + heatedBedController.pidDriveMax = HEATED_BED_PID_INTEGRAL_DRIVE_MAX; + heatedBedController.pidDriveMin = HEATED_BED_PID_INTEGRAL_DRIVE_MIN; + heatedBedController.pidPGain = HEATED_BED_PID_PGAIN; + heatedBedController.pidIGain = HEATED_BED_PID_IGAIN; + heatedBedController.pidDGain = HEATED_BED_PID_DGAIN; + heatedBedController.pidMax = HEATED_BED_PID_MAX; +#endif +#endif + printer_state.xLength = X_MAX_LENGTH; + printer_state.yLength = Y_MAX_LENGTH; + printer_state.zLength = Z_MAX_LENGTH; + printer_state.xMin = X_MIN_POS; + printer_state.yMin = Y_MIN_POS; + printer_state.zMin = Z_MIN_POS; +#if ENABLE_BACKLASH_COMPENSATION + printer_state.backlashX = X_BACKLASH; + printer_state.backlashY = Y_BACKLASH; + printer_state.backlashZ = Z_BACKLASH; +#endif + Extruder *e; +#if NUM_EXTRUDER>0 + e = &extruder[0]; + e->stepsPerMM = EXT0_STEPS_PER_MM; + e->maxFeedrate = EXT0_MAX_FEEDRATE; + e->maxStartFeedrate = EXT0_MAX_START_FEEDRATE; + e->maxAcceleration = EXT0_MAX_ACCELERATION; + e->tempControl.heatManager = EXT0_HEAT_MANAGER; +#ifdef TEMP_PID + e->tempControl.pidDriveMax = EXT0_PID_INTEGRAL_DRIVE_MAX; + e->tempControl.pidDriveMin = EXT0_PID_INTEGRAL_DRIVE_MIN; + e->tempControl.pidPGain = EXT0_PID_P; + e->tempControl.pidIGain = EXT0_PID_I; + e->tempControl.pidDGain = EXT0_PID_D; + e->tempControl.pidMax = EXT0_PID_MAX; +#endif + e->yOffset = EXT0_Y_OFFSET; + e->xOffset = EXT0_X_OFFSET; + e->watchPeriod = EXT0_WATCHPERIOD; +#if RETRACT_DURING_HEATUP + e->waitRetractTemperature = EXT0_WAIT_RETRACT_TEMP; + e->waitRetractUnits = EXT0_WAIT_RETRACT_UNITS; +#endif + e->coolerSpeed = EXT0_EXTRUDER_COOLER_SPEED; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + e->advanceK = EXT0_ADVANCE_K; +#endif + e->advanceL = EXT0_ADVANCE_L; +#endif +#endif // NUM_EXTRUDER>0 +#if NUM_EXTRUDER>1 + e = &extruder[1]; + e->stepsPerMM = EXT1_STEPS_PER_MM; + e->maxFeedrate = EXT1_MAX_FEEDRATE; + e->maxStartFeedrate = EXT1_MAX_START_FEEDRATE; + e->maxAcceleration = EXT1_MAX_ACCELERATION; + e->tempControl.heatManager = EXT1_HEAT_MANAGER; +#ifdef TEMP_PID + e->tempControl.pidDriveMax = EXT1_PID_INTEGRAL_DRIVE_MAX; + e->tempControl.pidDriveMin = EXT1_PID_INTEGRAL_DRIVE_MIN; + e->tempControl.pidPGain = EXT1_PID_P; + e->tempControl.pidIGain = EXT1_PID_I; + e->tempControl.pidDGain = EXT1_PID_D; + e->tempControl.pidMax = EXT1_PID_MAX; +#endif + e->yOffset = EXT1_Y_OFFSET; + e->xOffset = EXT1_X_OFFSET; + e->watchPeriod = EXT1_WATCHPERIOD; +#if RETRACT_DURING_HEATUP + e->waitRetractTemperature = EXT1_WAIT_RETRACT_TEMP; + e->waitRetractUnits = EXT1_WAIT_RETRACT_UNITS; +#endif + e->coolerSpeed = EXT1_EXTRUDER_COOLER_SPEED; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + e->advanceK = EXT1_ADVANCE_K; +#endif + e->advanceL = EXT1_ADVANCE_L; +#endif +#endif // NUM_EXTRUDER > 1 +#if NUM_EXTRUDER>2 + e = &extruder[2]; + e->stepsPerMM = EXT2_STEPS_PER_MM; + e->maxFeedrate = EXT2_MAX_FEEDRATE; + e->maxStartFeedrate = EXT2_MAX_START_FEEDRATE; + e->maxAcceleration = EXT2_MAX_ACCELERATION; + e->tempControl.heatManager = EXT2_HEAT_MANAGER; +#ifdef TEMP_PID + e->tempControl.pidDriveMax = EXT2_PID_INTEGRAL_DRIVE_MAX; + e->tempControl.pidDriveMin = EXT2_PID_INTEGRAL_DRIVE_MIN; + e->tempControl.pidPGain = EXT2_PID_P; + e->tempControl.pidIGain = EXT2_PID_I; + e->tempControl.pidDGain = EXT2_PID_D; + e->tempControl.pidMax = EXT2_PID_MAX; +#endif + e->yOffset = EXT2_Y_OFFSET; + e->xOffset = EXT2_X_OFFSET; + e->watchPeriod = EXT2_WATCHPERIOD; +#if RETRACT_DURING_HEATUP + e->waitRetractTemperature = EXT2_WAIT_RETRACT_TEMP; + e->waitRetractUnits = EXT2_WAIT_RETRACT_UNITS; +#endif + e->coolerSpeed = EXT2_EXTRUDER_COOLER_SPEED; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + e->advanceK = EXT2_ADVANCE_K; +#endif + e->advanceL = EXT2_ADVANCE_L; +#endif +#endif // NUM_EXTRUDER > 2 +#if NUM_EXTRUDER>3 + e = &extruder[3]; + e->stepsPerMM = EXT3_STEPS_PER_MM; + e->maxFeedrate = EXT3_MAX_FEEDRATE; + e->maxStartFeedrate = EXT3_MAX_START_FEEDRATE; + e->maxAcceleration = EXT3_MAX_ACCELERATION; + e->tempControl.heatManager = EXT3_HEAT_MANAGER; +#ifdef TEMP_PID + e->tempControl.pidDriveMax = EXT3_PID_INTEGRAL_DRIVE_MAX; + e->tempControl.pidDriveMin = EXT3_PID_INTEGRAL_DRIVE_MIN; + e->tempControl.pidPGain = EXT3_PID_P; + e->tempControl.pidIGain = EXT3_PID_I; + e->tempControl.pidDGain = EXT3_PID_D; + e->tempControl.pidMax = EXT3_PID_MAX; +#endif + e->yOffset = EXT3_Y_OFFSET; + e->xOffset = EXT3_X_OFFSET; + e->watchPeriod = EXT3_WATCHPERIOD; +#if RETRACT_DURING_HEATUP + e->waitRetractTemperature = EXT3_WAIT_RETRACT_TEMP; + e->waitRetractUnits = EXT3_WAIT_RETRACT_UNITS; +#endif + e->coolerSpeed = EXT3_EXTRUDER_COOLER_SPEED; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + e->advanceK = EXT3_ADVANCE_K; +#endif + e->advanceL = EXT3_ADVANCE_L; +#endif +#endif // NUM_EXTRUDER > 3 +#if NUM_EXTRUDER>4 + e = &extruder[4]; + e->stepsPerMM = EXT4_STEPS_PER_MM; + e->maxFeedrate = EXT4_MAX_FEEDRATE; + e->maxStartFeedrate = EXT4_MAX_START_FEEDRATE; + e->maxAcceleration = EXT4_MAX_ACCELERATION; + e->tempControl.heatManager = EXT4_HEAT_MANAGER; +#ifdef TEMP_PID + e->tempControl.pidDriveMax = EXT4_PID_INTEGRAL_DRIVE_MAX; + e->tempControl.pidDriveMin = EXT4_PID_INTEGRAL_DRIVE_MIN; + e->tempControl.pidPGain = EXT4_PID_P; + e->tempControl.pidIGain = EXT4_PID_I; + e->tempControl.pidDGain = EXT4_PID_D; + e->tempControl.pidMax = EXT4_PID_MAX; +#endif + e->yOffset = EXT4_Y_OFFSET; + e->xOffset = EXT4_X_OFFSET; + e->watchPeriod = EXT4_WATCHPERIOD; +#if RETRACT_DURING_HEATUP + e->waitRetractTemperature = EXT4_WAIT_RETRACT_TEMP; + e->waitRetractUnits = EXT4_WAIT_RETRACT_UNITS; +#endif + e->coolerSpeed = EXT4_EXTRUDER_COOLER_SPEED; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + e->advanceK = EXT4_ADVANCE_K; +#endif + e->advanceL = EXT4_ADVANCE_L; +#endif +#endif // NUM_EXTRUDER > 4 +#if NUM_EXTRUDER>5 + e = &extruder[5]; + e->stepsPerMM = EXT5_STEPS_PER_MM; + e->maxFeedrate = EXT5_MAX_FEEDRATE; + e->maxStartFeedrate = EXT5_MAX_START_FEEDRATE; + e->maxAcceleration = EXT5_MAX_ACCELERATION; + e->tempControl.heatManager = EXT5_HEAT_MANAGER; +#ifdef TEMP_PID + e->tempControl.pidDriveMax = EXT5_PID_INTEGRAL_DRIVE_MAX; + e->tempControl.pidDriveMin = EXT5_PID_INTEGRAL_DRIVE_MIN; + e->tempControl.pidPGain = EXT5_PID_P; + e->tempControl.pidIGain = EXT5_PID_I; + e->tempControl.pidDGain = EXT5_PID_D; + e->tempControl.pidMax = EXT5_PID_MAX; +#endif + e->yOffset = EXT5_Y_OFFSET; + e->xOffset = EXT5_X_OFFSET; + e->watchPeriod = EXT5_WATCHPERIOD; +#if RETRACT_DURING_HEATUP + e->waitRetractTemperature = EXT5_WAIT_RETRACT_TEMP; + e->waitRetractUnits = EXT5_WAIT_RETRACT_UNITS; +#endif + e->coolerSpeed = EXT5_EXTRUDER_COOLER_SPEED; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + e->advanceK = EXT5_ADVANCE_K; +#endif + e->advanceL = EXT5_ADVANCE_L; +#endif +#endif // NUM_EXTRUDER > 5 + extruder_select(current_extruder->id); + update_ramps_parameter(); + initHeatedBed(); +} +/** \brief Moves current settings to EEPROM. + +The values the are currently set are used to fill the eeprom.*/ +void epr_data_to_eeprom(byte corrupted) { + epr_set_long(EPR_BAUDRATE,baudrate); + epr_set_long(EPR_MAX_INACTIVE_TIME,max_inactive_time); + epr_set_long(EPR_STEPPER_INACTIVE_TIME,stepper_inactive_time); +//#define EPR_ACCELERATION_TYPE 1 + epr_set_float(EPR_XAXIS_STEPS_PER_MM,axis_steps_per_unit[0]); + epr_set_float(EPR_YAXIS_STEPS_PER_MM,axis_steps_per_unit[1]); + epr_set_float(EPR_ZAXIS_STEPS_PER_MM,axis_steps_per_unit[2]); + epr_set_float(EPR_X_MAX_FEEDRATE,max_feedrate[0]); + epr_set_float(EPR_Y_MAX_FEEDRATE,max_feedrate[1]); + epr_set_float(EPR_Z_MAX_FEEDRATE,max_feedrate[2]); + epr_set_float(EPR_X_HOMING_FEEDRATE,homing_feedrate[0]); + epr_set_float(EPR_Y_HOMING_FEEDRATE,homing_feedrate[1]); + epr_set_float(EPR_Z_HOMING_FEEDRATE,homing_feedrate[2]); + epr_set_float(EPR_MAX_JERK,printer_state.maxJerk); + epr_set_float(EPR_MAX_ZJERK,printer_state.maxZJerk); +#ifdef RAMP_ACCELERATION + epr_set_float(EPR_X_MAX_ACCEL,max_acceleration_units_per_sq_second[0]); + epr_set_float(EPR_Y_MAX_ACCEL,max_acceleration_units_per_sq_second[1]); + epr_set_float(EPR_Z_MAX_ACCEL,max_acceleration_units_per_sq_second[2]); + epr_set_float(EPR_X_MAX_TRAVEL_ACCEL,max_travel_acceleration_units_per_sq_second[0]); + epr_set_float(EPR_Y_MAX_TRAVEL_ACCEL,max_travel_acceleration_units_per_sq_second[1]); + epr_set_float(EPR_Z_MAX_TRAVEL_ACCEL,max_travel_acceleration_units_per_sq_second[2]); +#endif +#if USE_OPS==1 + epr_set_float(EPR_OPS_MIN_DISTANCE,printer_state.opsMinDistance); + epr_set_byte(EPR_OPS_MODE,printer_state.opsMode); + epr_set_float(EPR_OPS_MOVE_AFTER,printer_state.opsMoveAfter); + epr_set_float(EPR_OPS_RETRACT_DISTANCE,printer_state.opsRetractDistance); + epr_set_float(EPR_OPS_RETRACT_BACKLASH,printer_state.opsRetractBacklash); +#else + epr_set_float(EPR_OPS_MIN_DISTANCE,OPS_MIN_DISTANCE); + epr_set_byte(EPR_OPS_MODE,OPS_MODE); + epr_set_float(EPR_OPS_MOVE_AFTER,OPS_MOVE_AFTER); + epr_set_float(EPR_OPS_RETRACT_DISTANCE,OPS_RETRACT_DISTANCE); + epr_set_float(EPR_OPS_RETRACT_BACKLASH,OPS_RETRACT_BACKLASH); +#endif +#if HAVE_HEATED_BED + epr_set_byte(EPR_BED_HEAT_MANAGER,heatedBedController.heatManager); +#else + epr_set_byte(EPR_BED_HEAT_MANAGER,HEATED_BED_HEAT_MANAGER); +#endif +#if defined(TEMP_PID) && HAVE_HEATED_BED + epr_set_byte(EPR_BED_DRIVE_MAX,heatedBedController.pidDriveMax); + epr_set_byte(EPR_BED_DRIVE_MIN,heatedBedController.pidDriveMin); + epr_set_float(EPR_BED_PID_PGAIN,heatedBedController.pidPGain); + epr_set_float(EPR_BED_PID_IGAIN,heatedBedController.pidIGain); + epr_set_float(EPR_BED_PID_DGAIN,heatedBedController.pidDGain); + epr_set_byte(EPR_BED_PID_MAX,heatedBedController.pidMax); +#else + epr_set_byte(EPR_BED_DRIVE_MAX,HEATED_BED_PID_INTEGRAL_DRIVE_MAX); + epr_set_byte(EPR_BED_DRIVE_MIN,HEATED_BED_PID_INTEGRAL_DRIVE_MIN); + epr_set_float(EPR_BED_PID_PGAIN,HEATED_BED_PID_PGAIN); + epr_set_float(EPR_BED_PID_IGAIN,HEATED_BED_PID_IGAIN); + epr_set_float(EPR_BED_PID_DGAIN,HEATED_BED_PID_DGAIN); + epr_set_byte(EPR_BED_PID_MAX,HEATED_BED_PID_MAX); +#endif + epr_set_float(EPR_X_HOME_OFFSET,printer_state.xMin); + epr_set_float(EPR_Y_HOME_OFFSET,printer_state.yMin); + epr_set_float(EPR_Z_HOME_OFFSET,printer_state.zMin); + epr_set_float(EPR_X_LENGTH,printer_state.xLength); + epr_set_float(EPR_Y_LENGTH,printer_state.yLength); + epr_set_float(EPR_Z_LENGTH,printer_state.zLength); +#if ENABLE_BACKLASH_COMPENSATION + epr_set_float(EPR_BACKLASH_X,printer_state.backlashX); + epr_set_float(EPR_BACKLASH_Y,printer_state.backlashY); + epr_set_float(EPR_BACKLASH_Z,printer_state.backlashZ); +#else + epr_set_float(EPR_BACKLASH_X,0); + epr_set_float(EPR_BACKLASH_Y,0); + epr_set_float(EPR_BACKLASH_Z,0); +#endif + + // now the extruder + for(byte i=0;istepsPerMM); + epr_set_float(o+EPR_EXTRUDER_MAX_FEEDRATE,e->maxFeedrate); + epr_set_float(o+EPR_EXTRUDER_MAX_START_FEEDRATE,e->maxStartFeedrate); + epr_set_float(o+EPR_EXTRUDER_MAX_ACCELERATION,e->maxAcceleration); + epr_set_byte(o+EPR_EXTRUDER_HEAT_MANAGER,e->tempControl.heatManager); +#ifdef TEMP_PID + epr_set_byte(o+EPR_EXTRUDER_DRIVE_MAX,e->tempControl.pidDriveMax); + epr_set_byte(o+EPR_EXTRUDER_DRIVE_MIN,e->tempControl.pidDriveMin); + epr_set_float(o+EPR_EXTRUDER_PID_PGAIN,e->tempControl.pidPGain); + epr_set_float(o+EPR_EXTRUDER_PID_IGAIN,e->tempControl.pidIGain); + epr_set_float(o+EPR_EXTRUDER_PID_DGAIN,e->tempControl.pidDGain); + epr_set_byte(o+EPR_EXTRUDER_PID_MAX,e->tempControl.pidMax); +#endif + epr_set_long(o+EPR_EXTRUDER_X_OFFSET,e->xOffset); + epr_set_long(o+EPR_EXTRUDER_Y_OFFSET,e->yOffset); + epr_set_int(o+EPR_EXTRUDER_WATCH_PERIOD,e->watchPeriod); +#if RETRACT_DURING_HEATUP + epr_set_int(o+EPR_EXTRUDER_WAIT_RETRACT_TEMP,e->waitRetractTemperature); + epr_set_int(o+EPR_EXTRUDER_WAIT_RETRACT_UNITS,e->waitRetractUnits); +#else + epr_set_int(o+EPR_EXTRUDER_WAIT_RETRACT_TEMP,EXT0_WAIT_RETRACT_TEMP); + epr_set_int(o+EPR_EXTRUDER_WAIT_RETRACT_UNITS,EXT0_WAIT_RETRACT_UNITS); +#endif + epr_set_byte(o+EPR_EXTRUDER_COOLER_SPEED,e->coolerSpeed); +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + epr_set_float(o+EPR_EXTRUDER_ADVANCE_K,e->advanceK); +#else + epr_set_float(o+EPR_EXTRUDER_ADVANCE_K,0); +#endif + epr_set_float(o+EPR_EXTRUDER_ADVANCE_L,e->advanceL); +#else + epr_set_float(o+EPR_EXTRUDER_ADVANCE_K,0); + epr_set_float(o+EPR_EXTRUDER_ADVANCE_L,0); +#endif + } + if(corrupted) { + epr_set_long(EPR_PRINTING_TIME,0); + epr_set_float(EPR_PRINTING_DISTANCE,0); + } + // Save version and build checksum + epr_set_byte(EPR_VERSION,EEPROM_PROTOCOL_VERSION); + epr_set_byte(EPR_INTEGRITY_BYTE,epr_compute_checksum()); +} +/** \brief Copy data from EEPROM to variables. +*/ +void epr_eeprom_to_data() { + byte version = epr_get_byte(EPR_VERSION); // This is the saved version. Don't copy data not set in older versions! + baudrate = epr_get_long(EPR_BAUDRATE); + max_inactive_time = epr_get_long(EPR_MAX_INACTIVE_TIME); + stepper_inactive_time = epr_get_long(EPR_STEPPER_INACTIVE_TIME); +//#define EPR_ACCELERATION_TYPE 1 + axis_steps_per_unit[0] = epr_get_float(EPR_XAXIS_STEPS_PER_MM); + axis_steps_per_unit[1] = epr_get_float(EPR_YAXIS_STEPS_PER_MM); + axis_steps_per_unit[2] = epr_get_float(EPR_ZAXIS_STEPS_PER_MM); + max_feedrate[0] = epr_get_float(EPR_X_MAX_FEEDRATE); + max_feedrate[1] = epr_get_float(EPR_Y_MAX_FEEDRATE); + max_feedrate[2] = epr_get_float(EPR_Z_MAX_FEEDRATE); + homing_feedrate[0] = epr_get_float(EPR_X_HOMING_FEEDRATE); + homing_feedrate[1] = epr_get_float(EPR_Y_HOMING_FEEDRATE); + homing_feedrate[2] = epr_get_float(EPR_Z_HOMING_FEEDRATE); + printer_state.maxJerk = epr_get_float(EPR_MAX_JERK); + printer_state.maxZJerk = epr_get_float(EPR_MAX_ZJERK); +#ifdef RAMP_ACCELERATION + max_acceleration_units_per_sq_second[0] = epr_get_float(EPR_X_MAX_ACCEL); + max_acceleration_units_per_sq_second[1] = epr_get_float(EPR_Y_MAX_ACCEL); + max_acceleration_units_per_sq_second[2] = epr_get_float(EPR_Z_MAX_ACCEL); + max_travel_acceleration_units_per_sq_second[0] = epr_get_float(EPR_X_MAX_TRAVEL_ACCEL); + max_travel_acceleration_units_per_sq_second[1] = epr_get_float(EPR_Y_MAX_TRAVEL_ACCEL); + max_travel_acceleration_units_per_sq_second[2] = epr_get_float(EPR_Z_MAX_TRAVEL_ACCEL); +#endif +#if USE_OPS==1 + printer_state.opsMode = epr_get_byte(EPR_OPS_MODE); + printer_state.opsMoveAfter = epr_get_float(EPR_OPS_MOVE_AFTER); + printer_state.opsMinDistance = epr_get_float(EPR_OPS_MIN_DISTANCE); + printer_state.opsRetractDistance = epr_get_float(EPR_OPS_RETRACT_DISTANCE); + printer_state.opsRetractBacklash = epr_get_float(EPR_OPS_RETRACT_BACKLASH); +#endif +#if HAVE_HEATED_BED + heatedBedController.heatManager= epr_get_byte(EPR_BED_HEAT_MANAGER); +#ifdef TEMP_PID + heatedBedController.pidDriveMax = epr_get_byte(EPR_BED_DRIVE_MAX); + heatedBedController.pidDriveMin = epr_get_byte(EPR_BED_DRIVE_MIN); + heatedBedController.pidPGain = epr_get_float(EPR_BED_PID_PGAIN); + heatedBedController.pidIGain = epr_get_float(EPR_BED_PID_IGAIN); + heatedBedController.pidDGain = epr_get_float(EPR_BED_PID_DGAIN); + heatedBedController.pidMax = epr_get_byte(EPR_BED_PID_MAX); +#endif +#endif + printer_state.xMin = epr_get_float(EPR_X_HOME_OFFSET); + printer_state.yMin = epr_get_float(EPR_Y_HOME_OFFSET); + printer_state.zMin = epr_get_float(EPR_Z_HOME_OFFSET); + printer_state.xLength = epr_get_float(EPR_X_LENGTH); + printer_state.yLength = epr_get_float(EPR_Y_LENGTH); + printer_state.zLength = epr_get_float(EPR_Z_LENGTH); +#if ENABLE_BACKLASH_COMPENSATION + printer_state.backlashX = epr_get_float(EPR_BACKLASH_X); + printer_state.backlashY = epr_get_float(EPR_BACKLASH_Y); + printer_state.backlashZ = epr_get_float(EPR_BACKLASH_Z); +#endif + // now the extruder + for(byte i=0;istepsPerMM = epr_get_float(o+EPR_EXTRUDER_STEPS_PER_MM); + e->maxFeedrate = epr_get_float(o+EPR_EXTRUDER_MAX_FEEDRATE); + e->maxStartFeedrate = epr_get_float(o+EPR_EXTRUDER_MAX_START_FEEDRATE); + e->maxAcceleration = epr_get_float(o+EPR_EXTRUDER_MAX_ACCELERATION); + e->tempControl.heatManager = epr_get_byte(o+EPR_EXTRUDER_HEAT_MANAGER); +#ifdef TEMP_PID + e->tempControl.pidDriveMax = epr_get_byte(o+EPR_EXTRUDER_DRIVE_MAX); + e->tempControl.pidDriveMin = epr_get_byte(o+EPR_EXTRUDER_DRIVE_MIN); + e->tempControl.pidPGain = epr_get_float(o+EPR_EXTRUDER_PID_PGAIN); + e->tempControl.pidIGain = epr_get_float(o+EPR_EXTRUDER_PID_IGAIN); + e->tempControl.pidDGain = epr_get_float(o+EPR_EXTRUDER_PID_DGAIN); + e->tempControl.pidMax = epr_get_byte(o+EPR_EXTRUDER_PID_MAX); +#endif + e->xOffset = epr_get_long(o+EPR_EXTRUDER_X_OFFSET); + e->yOffset = epr_get_long(o+EPR_EXTRUDER_Y_OFFSET); + e->watchPeriod = epr_get_int(o+EPR_EXTRUDER_WATCH_PERIOD); +#if RETRACT_DURING_HEATUP + e->waitRetractTemperature = epr_get_int(o+EPR_EXTRUDER_WAIT_RETRACT_TEMP); + e->waitRetractUnits = epr_get_int(o+EPR_EXTRUDER_WAIT_RETRACT_UNITS); +#endif + #ifdef USE_ADVANCE + #ifdef ENABLE_QUADRATIC_ADVANCE + e->advanceK = epr_get_float(o+EPR_EXTRUDER_ADVANCE_K); +#endif + e->advanceL = epr_get_float(o+EPR_EXTRUDER_ADVANCE_L); + #endif + if(version>1) + e->coolerSpeed = epr_get_byte(o+EPR_EXTRUDER_COOLER_SPEED); + } + if(version!=EEPROM_PROTOCOL_VERSION) { + OUT_P_LN("Protocol version changed, upgrading"); + epr_data_to_eeprom(false); // Store new fields for changed version + } + extruder_select(current_extruder->id); + update_ramps_parameter(); + initHeatedBed(); +} +#endif +void epr_init_baudrate() { +#if EEPROM_MODE!=0 + if(epr_get_byte(EPR_MAGIC_BYTE)==EEPROM_MODE) { + baudrate = epr_get_long(EPR_BAUDRATE); + } +#endif +} +void epr_init() { +#if EEPROM_MODE!=0 + byte check = epr_compute_checksum(); + byte storedcheck = epr_get_byte(EPR_INTEGRITY_BYTE); + if(epr_get_byte(EPR_MAGIC_BYTE)==EEPROM_MODE && storedcheck==check) { + epr_eeprom_to_data(); + } else { + epr_set_byte(EPR_MAGIC_BYTE,EEPROM_MODE); // Make datachange permanent + epr_data_to_eeprom(storedcheck!=check); + } +#endif +} +/** +*/ +void epr_update_usage() { +#if EEPROM_MODE!=0 + if(printer_state.filamentPrinted==0) return; // No miles only enabled + unsigned long seconds = (millis()-printer_state.msecondsPrinting)/1000; + seconds += epr_get_long(EPR_PRINTING_TIME); + epr_set_long(EPR_PRINTING_TIME,seconds); + epr_set_float(EPR_PRINTING_DISTANCE,epr_get_float(EPR_PRINTING_DISTANCE)+printer_state.filamentPrinted*0.001); + OUT_P_F_LN("Adding filament:",printer_state.filamentPrinted); + printer_state.filamentPrinted = 0; + printer_state.msecondsPrinting = millis(); + byte newcheck = epr_compute_checksum(); + if(newcheck!=epr_get_byte(EPR_INTEGRITY_BYTE)) + epr_set_byte(EPR_INTEGRITY_BYTE,newcheck); +#endif +} + +/** \brief Writes all eeprom settings to serial console. + +For each value stored, this function generates one line with syntax + +EPR: pos type value description + +With +- pos = Position in EEPROM, the data starts. +- type = Value type: 0 = byte, 1 = int, 2 = long, 3 = float +- value = The value currently stored +- description = Definition of the value +*/ +void epr_output_settings() { +#if EEPROM_MODE!=0 + epr_out_long(EPR_BAUDRATE,PSTR("Baudrate")); + epr_out_float(EPR_PRINTING_DISTANCE,PSTR("Filament printed [m]")); + epr_out_long(EPR_PRINTING_TIME,PSTR("Printer active [s]")); + epr_out_long(EPR_MAX_INACTIVE_TIME,PSTR("Max. inactive time [ms,0=off]")); + epr_out_long(EPR_STEPPER_INACTIVE_TIME,PSTR("Stop stepper after inactivity [ms,0=off]")); +//#define EPR_ACCELERATION_TYPE 1 + epr_out_float(EPR_XAXIS_STEPS_PER_MM,PSTR("X-axis steps per mm")); + epr_out_float(EPR_YAXIS_STEPS_PER_MM,PSTR("Y-axis steps per mm")); + epr_out_float(EPR_ZAXIS_STEPS_PER_MM,PSTR("Z-axis steps per mm")); + epr_out_float(EPR_X_MAX_FEEDRATE,PSTR("X-axis max. feedrate [mm/s]")); + epr_out_float(EPR_Y_MAX_FEEDRATE,PSTR("Y-axis max. feedrate [mm/s]")); + epr_out_float(EPR_Z_MAX_FEEDRATE,PSTR("Z-axis max. feedrate [mm/s]")); + epr_out_float(EPR_X_HOMING_FEEDRATE,PSTR("X-axis homing feedrate [mm/s]")); + epr_out_float(EPR_Y_HOMING_FEEDRATE,PSTR("Y-axis homing feedrate [mm/s]")); + epr_out_float(EPR_Z_HOMING_FEEDRATE,PSTR("Z-axis homing feedrate [mm/s]")); + epr_out_float(EPR_MAX_JERK,PSTR("Max. jerk [mm/s]")); + epr_out_float(EPR_MAX_ZJERK,PSTR("Max. Z-jerk [mm/s]")); + epr_out_float(EPR_X_HOME_OFFSET,PSTR("X home pos [mm]")); + epr_out_float(EPR_Y_HOME_OFFSET,PSTR("Y home pos [mm]")); + epr_out_float(EPR_Z_HOME_OFFSET,PSTR("Z home pos [mm]")); + epr_out_float(EPR_X_LENGTH,PSTR("X max length [mm]")); + epr_out_float(EPR_Y_LENGTH,PSTR("Y max length [mm]")); + epr_out_float(EPR_Z_LENGTH,PSTR("Z max length [mm]")); +#if ENABLE_BACKLASH_COMPENSATION + epr_out_float(EPR_BACKLASH_X,PSTR("X backlash [mm]")); + epr_out_float(EPR_BACKLASH_Y,PSTR("Y backlash [mm]")); + epr_out_float(EPR_BACKLASH_Z,PSTR("Z backlash [mm]")); +#endif + +#ifdef RAMP_ACCELERATION + //epr_out_float(EPR_X_MAX_START_SPEED,PSTR("X-axis start speed [mm/s]")); + //epr_out_float(EPR_Y_MAX_START_SPEED,PSTR("Y-axis start speed [mm/s]")); + //epr_out_float(EPR_Z_MAX_START_SPEED,PSTR("Z-axis start speed [mm/s]")); + epr_out_float(EPR_X_MAX_ACCEL,PSTR("X-axis acceleration [mm/s^2]")); + epr_out_float(EPR_Y_MAX_ACCEL,PSTR("Y-axis acceleration [mm/s^2]")); + epr_out_float(EPR_Z_MAX_ACCEL,PSTR("Z-axis acceleration [mm/s^2]")); + epr_out_float(EPR_X_MAX_TRAVEL_ACCEL,PSTR("X-axis travel acceleration [mm/s^2]")); + epr_out_float(EPR_Y_MAX_TRAVEL_ACCEL,PSTR("Y-axis travel acceleration [mm/s^2]")); + epr_out_float(EPR_Z_MAX_TRAVEL_ACCEL,PSTR("Z-axis travel acceleration [mm/s^2]")); +#endif +#if USE_OPS==1 + epr_out_byte(EPR_OPS_MODE,PSTR("OPS operation mode [0=Off,1=Classic,2=Fast]")); + epr_out_float(EPR_OPS_MOVE_AFTER,PSTR("OPS move after x% retract [%]")); + epr_out_float(EPR_OPS_MIN_DISTANCE,PSTR("OPS min. distance for fil. retraction [mm]")); + epr_out_float(EPR_OPS_RETRACT_DISTANCE,PSTR("OPS retraction length [mm]")); + epr_out_float(EPR_OPS_RETRACT_BACKLASH,PSTR("OPS retraction backlash [mm]")); +#endif +#if HAVE_HEATED_BED + epr_out_byte(EPR_BED_HEAT_MANAGER,PSTR("Bed Heat Manager [0-2]")); +#ifdef TEMP_PID + epr_out_byte(EPR_BED_DRIVE_MAX,PSTR("Bed PID drive max")); + epr_out_byte(EPR_BED_DRIVE_MIN,PSTR("Bed PID drive min")); + epr_out_float(EPR_BED_PID_PGAIN,PSTR("Bed PID P-gain")); + epr_out_float(EPR_BED_PID_IGAIN,PSTR("Bed PID I-gain")); + epr_out_float(EPR_BED_PID_DGAIN,PSTR("Bed PID D-gain")); + epr_out_byte(EPR_BED_PID_MAX,PSTR("Bed PID max value [0-255]")); +#endif +#endif + // now the extruder + for(byte i=0;i. + +*/ + +#ifndef _EEPROM_H +#define _EEPROM_H + +#include + +// Id to distinguish version changes +#define EEPROM_PROTOCOL_VERSION 2 + +/** Where to start with our datablock in memory. Can be moved if you +have problems with other modules using the eeprom */ + +#define EEPROM_OFFSET 0 +#define EPR_MAGIC_BYTE 0 +#define EPR_ACCELERATION_TYPE 1 +#define EPR_XAXIS_STEPS_PER_MM 3 +#define EPR_YAXIS_STEPS_PER_MM 7 +#define EPR_ZAXIS_STEPS_PER_MM 11 +#define EPR_X_MAX_FEEDRATE 15 +#define EPR_Y_MAX_FEEDRATE 19 +#define EPR_Z_MAX_FEEDRATE 23 +#define EPR_X_HOMING_FEEDRATE 27 +#define EPR_Y_HOMING_FEEDRATE 31 +#define EPR_Z_HOMING_FEEDRATE 35 +#define EPR_MAX_JERK 39 +#define EPR_OPS_MIN_DISTANCE 43 +#define EPR_MAX_ZJERK 47 +#define EPR_X_MAX_ACCEL 51 +#define EPR_Y_MAX_ACCEL 55 +#define EPR_Z_MAX_ACCEL 59 +#define EPR_X_MAX_TRAVEL_ACCEL 63 +#define EPR_Y_MAX_TRAVEL_ACCEL 67 +#define EPR_Z_MAX_TRAVEL_ACCEL 71 +#define EPR_BAUDRATE 75 +#define EPR_MAX_INACTIVE_TIME 79 +#define EPR_STEPPER_INACTIVE_TIME 83 +#define EPR_OPS_RETRACT_DISTANCE 87 +#define EPR_OPS_RETRACT_BACKLASH 91 +#define EPR_EXTRUDER_SPEED 95 +#define EPR_OPS_MOVE_AFTER 99 +#define EPR_OPS_MODE 103 +#define EPR_INTEGRITY_BYTE 104 // Here the xored sum over eeprom is stored +#define EPR_VERSION 105 // Version id for updates in EEPROM storage +#define EPR_BED_HEAT_MANAGER 106 +#define EPR_BED_DRIVE_MAX 107 +#define EPR_BED_PID_PGAIN 108 +#define EPR_BED_PID_IGAIN 112 +#define EPR_BED_PID_DGAIN 116 +#define EPR_BED_PID_MAX 120 +#define EPR_BED_DRIVE_MIN 124 +#define EPR_PRINTING_TIME 125 // Time in seconds printing +#define EPR_PRINTING_DISTANCE 129 // Filament length printed +#define EPR_X_HOME_OFFSET 133 +#define EPR_Y_HOME_OFFSET 137 +#define EPR_Z_HOME_OFFSET 141 +#define EPR_X_LENGTH 145 +#define EPR_Y_LENGTH 149 +#define EPR_Z_LENGTH 153 +#define EPR_BACKLASH_X 157 +#define EPR_BACKLASH_Y 161 +#define EPR_BACKLASH_Z 165 + +#define EEPROM_EXTRUDER_OFFSET 200 +// bytes per extruder needed, leave some space for future development +#define EEPROM_EXTRUDER_LENGTH 100 +// Extruder positions relative to extruder start +#define EPR_EXTRUDER_STEPS_PER_MM 0 +#define EPR_EXTRUDER_MAX_FEEDRATE 4 +// Feedrate from halted extruder in mm/s +#define EPR_EXTRUDER_MAX_START_FEEDRATE 8 +// Acceleration in mm/s^2 +#define EPR_EXTRUDER_MAX_ACCELERATION 12 +#define EPR_EXTRUDER_HEAT_MANAGER 16 +#define EPR_EXTRUDER_DRIVE_MAX 17 +#define EPR_EXTRUDER_PID_PGAIN 18 +#define EPR_EXTRUDER_PID_IGAIN 22 +#define EPR_EXTRUDER_PID_DGAIN 26 +#define EPR_EXTRUDER_PID_MAX 30 +#define EPR_EXTRUDER_X_OFFSET 31 +#define EPR_EXTRUDER_Y_OFFSET 35 +#define EPR_EXTRUDER_WATCH_PERIOD 39 +#define EPR_EXTRUDER_ADVANCE_K 41 +#define EPR_EXTRUDER_DRIVE_MIN 45 +#define EPR_EXTRUDER_ADVANCE_L 46 +#define EPR_EXTRUDER_WAIT_RETRACT_TEMP 50 +#define EPR_EXTRUDER_WAIT_RETRACT_UNITS 52 +#define EPR_EXTRUDER_COOLER_SPEED 54 +#if EEPROM_MODE!=0 + +extern inline void epr_set_byte(uint pos,byte value); +extern inline void epr_set_int(uint pos,int value); +extern inline void epr_set_long(uint pos,long value); +extern inline void epr_set_float(uint pos,float value); +extern void epr_data_to_eeprom(byte corrupted); +extern void epr_eeprom_to_data(); +extern void epr_eeprom_reset(); + +inline byte epr_get_byte(uint pos) { + return eeprom_read_byte ((unsigned char *)(EEPROM_OFFSET+pos)); +} +inline int epr_get_int(uint pos) { + return eeprom_read_word((unsigned int *)(EEPROM_OFFSET+pos)); +} +inline long epr_get_long(uint pos) { + return eeprom_read_dword((unsigned long*)(EEPROM_OFFSET+pos)); +} +inline float epr_get_float(uint pos) { + float v; + eeprom_read_block(&v,(void *)(EEPROM_OFFSET+pos),4); // newer gcc have eeprom_read_block but not arduino 22 + return v; +} +#endif + +extern void epr_output_settings(); +extern void epr_update(GCode *com); +extern void epr_init(); +extern void epr_init_baudrate(); +extern void epr_update_usage(); +#endif + diff --git a/Repetier/Extruder.cpp b/Repetier/Extruder.cpp new file mode 100644 index 0000000..a3a14c9 --- /dev/null +++ b/Repetier/Extruder.cpp @@ -0,0 +1,1082 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. +*/ + +#include "Reptier.h" +#include "pins_arduino.h" +#include "ui.h" +#if EEPROM_MODE!=0 +#include "Eeprom.h" +#endif + +Extruder *current_extruder; + +#if NUM_EXTRUDER>0 +prog_char ext0_select_cmd[] PROGMEM = EXT0_SELECT_COMMANDS; +prog_char ext0_deselect_cmd[] PROGMEM = EXT0_DESELECT_COMMANDS; +#endif +#if NUM_EXTRUDER>1 +prog_char ext1_select_cmd[] PROGMEM = EXT1_SELECT_COMMANDS; +prog_char ext1_deselect_cmd[] PROGMEM = EXT1_DESELECT_COMMANDS; +#endif +#if NUM_EXTRUDER>2 +prog_char ext2_select_cmd[] PROGMEM = EXT2_SELECT_COMMANDS; +prog_char ext2_deselect_cmd[] PROGMEM = EXT2_DESELECT_COMMANDS; +#endif +#if NUM_EXTRUDER>3 +prog_char ext3_select_cmd[] PROGMEM = EXT3_SELECT_COMMANDS; +prog_char ext3_deselect_cmd[] PROGMEM = EXT3_DESELECT_COMMANDS; +#endif +#if NUM_EXTRUDER>4 +prog_char ext4_select_cmd[] PROGMEM = EXT4_SELECT_COMMANDS; +prog_char ext4_deselect_cmd[] PROGMEM = EXT4_DESELECT_COMMANDS; +#endif +#if NUM_EXTRUDER>5 +prog_char ext5_select_cmd[] PROGMEM = EXT5_SELECT_COMMANDS; +prog_char ext5_deselect_cmd[] PROGMEM = EXT5_DESELECT_COMMANDS; +#endif + +Extruder extruder[NUM_EXTRUDER] = { +#if NUM_EXTRUDER>0 +{0,EXT0_X_OFFSET,EXT0_Y_OFFSET,EXT0_STEPS_PER_MM,EXT0_ENABLE_PIN,EXT0_ENABLE_ON, + EXT0_MAX_FEEDRATE,EXT0_MAX_ACCELERATION,EXT0_MAX_START_FEEDRATE,0,EXT0_WATCHPERIOD + ,EXT0_WAIT_RETRACT_TEMP,EXT0_WAIT_RETRACT_UNITS +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + ,EXT0_ADVANCE_K +#endif + ,EXT0_ADVANCE_L +#endif + ,{0,EXT0_TEMPSENSOR_TYPE,EXT0_SENSOR_INDEX,0,0,0,0,0,EXT0_HEAT_MANAGER +#ifdef TEMP_PID + ,0,EXT0_PID_INTEGRAL_DRIVE_MAX,EXT0_PID_INTEGRAL_DRIVE_MIN,EXT0_PID_P,EXT0_PID_I,EXT0_PID_D,EXT0_PID_MAX,0,0,0,{0,0,0,0} +#endif + } + ,ext0_select_cmd,ext0_deselect_cmd,EXT0_EXTRUDER_COOLER_SPEED,0 + } +#endif +#if NUM_EXTRUDER>1 + ,{1,EXT1_X_OFFSET,EXT1_Y_OFFSET,EXT1_STEPS_PER_MM,EXT1_ENABLE_PIN,EXT1_ENABLE_ON, + EXT1_MAX_FEEDRATE,EXT1_MAX_ACCELERATION,EXT1_MAX_START_FEEDRATE,0,EXT1_WATCHPERIOD + ,EXT1_WAIT_RETRACT_TEMP,EXT1_WAIT_RETRACT_UNITS +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + ,EXT1_ADVANCE_K +#endif + ,EXT1_ADVANCE_L +#endif + ,{1,EXT1_TEMPSENSOR_TYPE,EXT1_SENSOR_INDEX,0,0,0,0,0,EXT1_HEAT_MANAGER +#ifdef TEMP_PID + ,0,EXT1_PID_INTEGRAL_DRIVE_MAX,EXT1_PID_INTEGRAL_DRIVE_MIN,EXT1_PID_P,EXT1_PID_I,EXT1_PID_D,EXT1_PID_MAX,0,0,0,{0,0,0,0} +#endif + } + ,ext1_select_cmd,ext1_deselect_cmd,EXT1_EXTRUDER_COOLER_SPEED,0 + } +#endif +#if NUM_EXTRUDER>2 + ,{2,EXT2_X_OFFSET,EXT2_Y_OFFSET,EXT2_STEPS_PER_MM,EXT2_ENABLE_PIN,EXT2_ENABLE_ON, + EXT2_MAX_FEEDRATE,EXT2_MAX_ACCELERATION,EXT2_MAX_START_FEEDRATE,0,EXT2_WATCHPERIOD + ,EXT2_WAIT_RETRACT_TEMP,EXT2_WAIT_RETRACT_UNITS +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + ,EXT2_ADVANCE_K +#endif + ,EXT2_ADVANCE_L +#endif + ,{2,EXT2_TEMPSENSOR_TYPE,EXT2_SENSOR_INDEX,0,0,0,0,0,EXT2_HEAT_MANAGER +#ifdef TEMP_PID + ,0,EXT2_PID_INTEGRAL_DRIVE_MAX,EXT2_PID_INTEGRAL_DRIVE_MIN,EXT2_PID_P,EXT2_PID_I,EXT2_PID_D,EXT2_PID_MAX,0,0,0,{0,0,0,0} +#endif + } + ,ext2_select_cmd,ext2_deselect_cmd,EXT2_EXTRUDER_COOLER_SPEED,0 + } +#endif +#if NUM_EXTRUDER>3 + ,{3,EXT3_X_OFFSET,EXT3_Y_OFFSET,EXT3_STEPS_PER_MM,EXT3_ENABLE_PIN,EXT3_ENABLE_ON, + EXT3_MAX_FEEDRATE,EXT3_MAX_ACCELERATION,EXT3_MAX_START_FEEDRATE,0,EXT3_WATCHPERIOD + ,EXT3_WAIT_RETRACT_TEMP,EXT3_WAIT_RETRACT_UNITS +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + ,EXT3_ADVANCE_K +#endif + ,EXT3_ADVANCE_L +#endif + ,{3,EXT3_TEMPSENSOR_TYPE,EXT3_SENSOR_INDEX,0,0,0,0,0,EXT3_HEAT_MANAGER +#ifdef TEMP_PID + ,0,EXT3_PID_INTEGRAL_DRIVE_MAX,EXT3_PID_INTEGRAL_DRIVE_MIN,EXT3_PID_P,EXT3_PID_I,EXT3_PID_D,EXT3_PID_MAX,0,0,0,{0,0,0,0} +#endif + } + ,ext3_select_cmd,ext3_deselect_cmd,EXT3_EXTRUDER_COOLER_SPEED,0 + } +#endif +#if NUM_EXTRUDER>4 + ,{4,EXT4_X_OFFSET,EXT4_Y_OFFSET,EXT4_STEPS_PER_MM,EXT4_ENABLE_PIN,EXT4_ENABLE_ON, + EXT4_MAX_FEEDRATE,EXT4_MAX_ACCELERATION,EXT4_MAX_START_FEEDRATE,0,EXT4_WATCHPERIOD + ,EXT4_WAIT_RETRACT_TEMP,EXT4_WAIT_RETRACT_UNITS +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + ,EXT4_ADVANCE_K +#endif + ,EXT4_ADVANCE_L +#endif + ,{4,EXT4_TEMPSENSOR_TYPE,EXT4_SENSOR_INDEX,0,0,0,0,0,EXT4_HEAT_MANAGER +#ifdef TEMP_PID + ,0,EXT4_PID_INTEGRAL_DRIVE_MAX,EXT4_PID_INTEGRAL_DRIVE_MIN,EXT4_PID_P,EXT4_PID_I,EXT4_PID_D,EXT4_PID_MAX,0,0,0,{0,0,0,0} +#endif + } + ,ext4_select_cmd,ext4_deselect_cmd,EXT4_EXTRUDER_COOLER_SPEED,0 + } +#endif +#if NUM_EXTRUDER>5 + ,{5,EXT5_X_OFFSET,EXT5_Y_OFFSET,EXT5_STEPS_PER_MM,EXT5_ENABLE_PIN,EXT5_ENABLE_ON, + EXT5_MAX_FEEDRATE,EXT5_MAX_ACCELERATION,EXT5_MAX_START_FEEDRATE,0,EXT5_WATCHPERIOD + ,EXT5_WAIT_RETRACT_TEMP,EXT5_WAIT_RETRACT_UNITS +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + ,EXT5_ADVANCE_K +#endif + ,EXT5_ADVANCE_L +#endif + ,{5,EXT5_TEMPSENSOR_TYPE,EXT5_SENSOR_INDEX,0,0,0,0,0,EXT5_HEAT_MANAGER +#ifdef TEMP_PID + ,0,EXT5_PID_INTEGRAL_DRIVE_MAX,EXT5_PID_INTEGRAL_DRIVE_MIN,EXT5_PID_P,EXT5_PID_I,EXT5_PID_D,EXT5_PID_MAX,0,0,0,{0,0,0,0} +#endif + } + ,ext5_select_cmd,ext5_deselect_cmd,EXT5_EXTRUDER_COOLER_SPEED,0 + } +#endif +}; + +#if HAVE_HEATED_BED +#define NUM_TEMPERATURE_LOOPS NUM_EXTRUDER+1 +TemperatureController heatedBedController = {NUM_EXTRUDER,HEATED_BED_SENSOR_TYPE,BED_SENSOR_INDEX,0,0,0,0,0,HEATED_BED_HEAT_MANAGER +#ifdef TEMP_PID +,0,HEATED_BED_PID_INTEGRAL_DRIVE_MAX,HEATED_BED_PID_INTEGRAL_DRIVE_MIN,HEATED_BED_PID_PGAIN,HEATED_BED_PID_IGAIN,HEATED_BED_PID_DGAIN,HEATED_BED_PID_MAX,0,0,0,{0,0,0,0} +#endif +}; +#else +#define NUM_TEMPERATURE_LOOPS NUM_EXTRUDER +#endif + +TemperatureController *tempController[NUM_TEMPERATURE_LOOPS] = { +#if NUM_EXTRUDER>0 + &extruder[0].tempControl +#endif +#if NUM_EXTRUDER>1 +,&extruder[1].tempControl +#endif +#if NUM_EXTRUDER>2 +,&extruder[2].tempControl +#endif +#if NUM_EXTRUDER>3 +,&extruder[3].tempControl +#endif +#if NUM_EXTRUDER>4 +,&extruder[4].tempControl +#endif +#if NUM_EXTRUDER>5 +,&extruder[5].tempControl +#endif +#if HAVE_HEATED_BED +,&heatedBedController +#endif +}; +#ifdef USE_GENERIC_THERMISTORTABLE_1 +short temptable_generic1[GENERIC_THERM_NUM_ENTRIES][2]; +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_2 +short temptable_generic2[GENERIC_THERM_NUM_ENTRIES][2]; +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_3 +short temptable_generic3[GENERIC_THERM_NUM_ENTRIES][2]; +#endif + +byte manage_monitor = 255; ///< Temp. we want to monitor with our host. 1+NUM_EXTRUDER is heated bed +unsigned int counter_periodical=0; +volatile byte execute_periodical=0; +byte counter_250ms=25; + +#ifdef SUPPORT_MAX6675 +extern int read_max6675(byte ss_pin); +#endif + +#if ANALOG_INPUTS>0 +const uint8 osAnalogInputChannels[] PROGMEM = ANALOG_INPUT_CHANNELS; +uint8 osAnalogInputCounter[ANALOG_INPUTS]; +uint osAnalogInputBuildup[ANALOG_INPUTS]; +uint8 osAnalogInputPos=0; // Current sampling position +volatile uint osAnalogInputValues[ANALOG_INPUTS]; +#endif + +void initHeatedBed() { +#if HAVE_HEATED_BED +#ifdef TEMP_PID + if(heatedBedController.pidIGain) { // prevent division by zero + heatedBedController.tempIStateLimitMax = (float)heatedBedController.pidDriveMax*10.0f/heatedBedController.pidIGain; + heatedBedController.tempIStateLimitMin = (float)heatedBedController.pidDriveMin*10.0f/heatedBedController.pidIGain; + } +#endif +#endif +} + +// ------------------------------------------------------------------------------------------------------------------ +// ------------------------------------------- createGenericTable --------------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ +#if defined(USE_GENERIC_THERMISTORTABLE_1) || defined(USE_GENERIC_THERMISTORTABLE_2) || defined(USE_GENERIC_THERMISTORTABLE_3) +void createGenericTable(short table[GENERIC_THERM_NUM_ENTRIES][2],short minTemp,short maxTemp,float beta,float r0,float t0,float r1,float r2) { + t0+=273.15f; + float rs,vs; + if(r1==0) { + rs = r2; + vs = GENERIC_THERM_VREF; + } else { + vs =(float)(GENERIC_THERM_VREF*r1)/(r1+r2); + rs = (r2*r1)/(r1+r2); + } + float k = r0*exp(-beta/t0); + float delta = (maxTemp-minTemp)/(GENERIC_THERM_NUM_ENTRIES-1.0f); + for(byte i=0;i4092) adc=4092; + table[i][0] = (adc>>(ANALOG_REDUCE_BITS)); + table[i][1] = (int)t; +#ifdef DEBUG_GENERIC + OUT_P_I("GenTemp: ",table[i][0]); + OUT_P_I_LN(",",table[i][1]); +#endif + } +} +#endif + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- initExtruder ------------------------------------------------------ +// ------------------------------------------------------------------------------------------------------------------ + +/** \brief Initalizes all extruder. + +Updates the pin configuration needed for the extruder and activates extruder 0. +Starts a interrupt based analog input reader, which is used by simple thermistors +for temperature reading. +*/ +void initExtruder() { + byte i; + current_extruder = &extruder[0]; +#ifdef USE_GENERIC_THERMISTORTABLE_1 + createGenericTable(temptable_generic1,GENERIC_THERM1_MIN_TEMP,GENERIC_THERM1_MAX_TEMP,GENERIC_THERM1_BETA,GENERIC_THERM1_R0,GENERIC_THERM1_T0,GENERIC_THERM1_R1,GENERIC_THERM1_R2); +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_2 + createGenericTable(temptable_generic2,GENERIC_THERM2_MIN_TEMP,GENERIC_THERM2_MAX_TEMP,GENERIC_THERM2_BETA,GENERIC_THERM2_R0,GENERIC_THERM2_T0,GENERIC_THERM2_R1,GENERIC_THERM2_R2); +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_3 + createGenericTable(temptable_generic3,GENERIC_THERM3_MIN_TEMP,GENERIC_THERM3_MAX_TEMP,GENERIC_THERM3_BETA,GENERIC_THERM3_R0,GENERIC_THERM3_T0,GENERIC_THERM3_R1,GENERIC_THERM3_R2); +#endif +#if defined(EXT0_STEP_PIN) && EXT0_STEP_PIN>-1 + SET_OUTPUT(EXT0_DIR_PIN); + SET_OUTPUT(EXT0_STEP_PIN); +#endif +#if defined(EXT1_STEP_PIN) && EXT1_STEP_PIN>-1 && NUM_EXTRUDER>1 + SET_OUTPUT(EXT1_DIR_PIN); + SET_OUTPUT(EXT1_STEP_PIN); +#endif +#if defined(EXT2_STEP_PIN) && EXT2_STEP_PIN>-1 && NUM_EXTRUDER>2 + SET_OUTPUT(EXT2_DIR_PIN); + SET_OUTPUT(EXT2_STEP_PIN); +#endif +#if defined(EXT3_STEP_PIN) && EXT3_STEP_PIN>-1 && NUM_EXTRUDER>3 + SET_OUTPUT(EXT3_DIR_PIN); + SET_OUTPUT(EXT3_STEP_PIN); +#endif +#if defined(EXT4_STEP_PIN) && EXT4_STEP_PIN>-1 && NUM_EXTRUDER>4 + SET_OUTPUT(EXT4_DIR_PIN); + SET_OUTPUT(EXT4_STEP_PIN); +#endif +#if defined(EXT5_STEP_PIN) && EXT5_STEP_PIN>-1 && NUM_EXTRUDER>5 + SET_OUTPUT(EXT5_DIR_PIN); + SET_OUTPUT(EXT5_STEP_PIN); +#endif + + for(i=0;ienablePin > -1) { + pinMode(act->enablePin,OUTPUT); + if(!act->enableOn) digitalWrite(act->enablePin,HIGH); + } + act->tempControl.lastTemperatureUpdate = millis(); +#ifdef SUPPORT_MAX6675 + if(act->sensorType==101) { + WRITE(SCK_PIN,0); + SET_OUTPUT(SCK_PIN); + WRITE(MOSI_PIN,1); + SET_OUTPUT(MOSI_PIN); + WRITE(MISO_PIN,1); + SET_INPUT(MISO_PIN); + digitalWrite(act->tempControl.sensorPin,1); + pinMode(act->tempControl.sensorPin,OUTPUT); + } +#endif + } +#if HEATED_BED_HEATER_PIN>-1 + SET_OUTPUT(HEATED_BED_HEATER_PIN); + initHeatedBed(); +#endif + extruder_select(0); +#if ANALOG_INPUTS>0 + ADMUX = ANALOG_REF; // refernce voltage + for(i=0;ipidIGain) { // prevent division by zero + tc->tempIStateLimitMax = (float)tc->pidDriveMax*10.0f/tc->pidIGain; + tc->tempIStateLimitMin = (float)tc->pidDriveMin*10.0f/tc->pidIGain; + } +#endif +} +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- extruder_select --------------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +/** \brief Select extruder ext_num. + +This function changes and initalizes a new extruder. This is also called, after the eeprom values are changed. +*/ +void extruder_select(byte ext_num) { + if(ext_num>=NUM_EXTRUDER) + ext_num = 0; +#if NUM_EXTRUDER>1 + bool executeSelect = false; + if(ext_num!=current_extruder->id) { + gcode_execute_PString(current_extruder->deselectCommands); + executeSelect = true; + } +#endif + current_extruder->extrudePosition = printer_state.currentPositionSteps[3]; + long dx = current_extruder->xOffset; + long dy = current_extruder->yOffset; + current_extruder = &extruder[ext_num]; + dx -= current_extruder->xOffset; + dy -= current_extruder->yOffset; +#ifdef SEPERATE_EXTRUDER_POSITIONS + // Use seperate extruder positions only if beeing told. Slic3r e.g. creates a continuous extruder position increment + printer_state.currentPositionSteps[3] = current_extruder->extrudePosition; +#endif + printer_state.destinationSteps[3] = printer_state.currentPositionSteps[3]; + axis_steps_per_unit[3] = current_extruder->stepsPerMM; + inv_axis_steps_per_unit[3] = 1.0f/axis_steps_per_unit[3]; + max_feedrate[3] = current_extruder->maxFeedrate; +// max_start_speed_units_per_second[3] = current_extruder->maxStartFeedrate; + max_acceleration_units_per_sq_second[3] = max_travel_acceleration_units_per_sq_second[3] = current_extruder->maxAcceleration; + axis_travel_steps_per_sqr_second[3] = axis_steps_per_sqr_second[3] = max_acceleration_units_per_sq_second[3] * axis_steps_per_unit[3]; +#if USE_OPS==1 || defined(USE_ADVANCE) + printer_state.minExtruderSpeed = (byte)floor(F_CPU/(TIMER0_PRESCALE*current_extruder->maxStartFeedrate*current_extruder->stepsPerMM)); + printer_state.maxExtruderSpeed = (byte)floor(F_CPU/(TIMER0_PRESCALE*current_extruder->maxFeedrate*current_extruder->stepsPerMM)); + if(printer_state.maxExtruderSpeed>15) printer_state.maxExtruderSpeed = 15; + if(printer_state.maxExtruderSpeed>=printer_state.minExtruderSpeed) { + printer_state.maxExtruderSpeed = printer_state.minExtruderSpeed; + } else { + float maxdist = current_extruder->maxFeedrate*current_extruder->maxFeedrate*0.00013888/current_extruder->maxAcceleration; + maxdist-= current_extruder->maxStartFeedrate*current_extruder->maxStartFeedrate*0.5/current_extruder->maxAcceleration; + printer_state.extruderAccelerateDelay = (byte)constrain(ceil(maxdist*current_extruder->stepsPerMM/(printer_state.minExtruderSpeed-printer_state.maxExtruderSpeed)),1,255); + } + float fmax=((float)F_CPU/((float)printer_state.maxExtruderSpeed*TIMER0_PRESCALE*axis_steps_per_unit[3]))*60.0; // Limit feedrate to interrupt speed + if(fmaxtempControl); +#if USE_OPS==1 + printer_state.opsRetractSteps = printer_state.opsRetractDistance*current_extruder->stepsPerMM; + printer_state.opsPushbackSteps = (printer_state.opsRetractDistance+printer_state.opsRetractBacklash)*current_extruder->stepsPerMM; + if(printer_state.opsMode<=1) + printer_state.opsMoveAfterSteps = 0; + else + printer_state.opsMoveAfterSteps = (int)(-(float)printer_state.opsRetractSteps*(100.0-printer_state.opsMoveAfter)*0.01); +#endif + if(dx || dy) { + float oldfeedrate = printer_state.feedrate; + move_steps(dx,dy,0,0,homing_feedrate[0],true,ALWAYS_CHECK_ENDSTOPS); + printer_state.offsetX += dx; + printer_state.offsetY += dy; + printer_state.feedrate = oldfeedrate; + } +#if NUM_EXTRUDER>1 + if(executeSelect) // Run only when changing + gcode_execute_PString(current_extruder->selectCommands); +#endif +} + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- extruder_set_temperature ------------------------------------------ +// ------------------------------------------------------------------------------------------------------------------ + +void extruder_set_temperature(float temp_celsius,byte extr) { + bool alloffs = true; + for(byte i=0;itargetTemperatureC>15) alloffs = false; +#ifdef MAXTEMP + if(temp_celsius>MAXTEMP) temp_celsius = MAXTEMP; +#endif + if(temp_celsius<0) temp_celsius=0; + TemperatureController *tc = tempController[extr]; + if(temp_celsius==tc->targetTemperatureC) return; + tc->targetTemperature = conv_temp_raw(tc->sensorType,temp_celsius); + tc->targetTemperatureC = temp_celsius; + if(temp_celsius<50) extruder[extr].coolerPWM = 0; + else extruder[extr].coolerPWM = extruder[extr].coolerSpeed; + OUT_P_FX("TargetExtr",extr,0); + OUT_P_FX_LN(":",temp_celsius,0); +#if USE_OPS==1 + if(extr==current_extruder->id && temp_celsius<(MIN_EXTRUDER_TEMP)) { // Protect for cold filament + printer_state.filamentRetracted = false; + printmoveSeen = 0; + } +#endif + bool alloff = true; + for(byte i=0;itargetTemperatureC>15) alloff = false; +#if EEPROM_MODE != 0 + if(alloff && !alloffs) // All heaters are now switched off? + epr_update_usage(); +#endif + if(alloffs && !alloff) // heaters are turned on, start measuring printing time + printer_state.msecondsPrinting = millis(); +} + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- heated_bed_set_temperature ---------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +void heated_bed_set_temperature(float temp_celsius) { +#if HAVE_HEATED_BED + if(temp_celsius>HEATED_BED_MAX_TEMP) temp_celsius = HEATED_BED_MAX_TEMP; + if(temp_celsius<0) temp_celsius = 0; + if(heatedBedController.targetTemperatureC==temp_celsius) return; // don't flood log with messages if killed + heatedBedController.targetTemperatureC=temp_celsius; + heatedBedController.targetTemperature = conv_temp_raw(heatedBedController.sensorType,temp_celsius); + OUT_P_FX_LN("TargetBed:",heatedBedController.targetTemperatureC,0); +#endif +} + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- heated_bed_get_temperature ---------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +float heated_bed_get_temperature() { +#if HAVE_HEATED_BED + TemperatureController *c = tempController[NUM_TEMPERATURE_LOOPS-1]; + return c->currentTemperatureC; + //return conv_raw_temp(c->sensorType,c->currentTemperature); +#else + return -1; +#endif +} + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- extruder_disable -------------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +/** \brief Disable stepper motor of current extruder. */ +void extruder_disable() { + if(current_extruder->enablePin > -1) + digitalWrite(current_extruder->enablePin,!current_extruder->enableOn); +} +#define NUMTEMPS_1 28 +// Epcos B57560G0107F000 +const short temptable_1[NUMTEMPS_1][2] PROGMEM = { +{0,4000},{92,2400},{105,2320},{121,2240},{140,2160},{162,2080},{189,2000},{222,1920},{261,1840},{308,1760}, +{365,1680},{434,1600},{519,1520},{621,1440},{744,1360},{891,1280},{1067,1200},{1272,1120}, +{1771,960},{2357,800},{2943,640},{3429,480},{3760,320},{3869,240},{3912,200},{3948,160},{4077,-160},{4094,-440} +}; +#define NUMTEMPS_2 21 +const short temptable_2[NUMTEMPS_2][2] PROGMEM = { + {1*4, 848*8},{54*4, 275*8}, {107*4, 228*8}, {160*4, 202*8},{213*4, 185*8}, {266*4, 171*8}, {319*4, 160*8}, {372*4, 150*8}, + {425*4, 141*8}, {478*4, 133*8},{531*4, 125*8},{584*4, 118*8},{637*4, 110*8},{690*4, 103*8},{743*4, 95*8},{796*4, 86*8}, + {849*4, 77*8},{902*4, 65*8},{955*4, 49*8},{1008*4, 17*8},{1020*4, 0*8} //safety +}; + +#define NUMTEMPS_3 28 +const short temptable_3[NUMTEMPS_3][2] PROGMEM = { + {1*4,864*8},{21*4,300*8},{25*4,290*8},{29*4,280*8},{33*4,270*8},{39*4,260*8},{46*4,250*8},{54*4,240*8},{64*4,230*8},{75*4,220*8}, + {90*4,210*8},{107*4,200*8},{128*4,190*8},{154*4,180*8},{184*4,170*8},{221*4,160*8},{265*4,150*8},{316*4,140*8},{375*4,130*8}, + {441*4,120*8},{513*4,110*8},{588*4,100*8},{734*4,80*8},{856*4,60*8},{938*4,40*8},{986*4,20*8},{1008*4,0*8},{1018*4,-20*8} }; + +#define NUMTEMPS_4 20 +const short temptable_4[NUMTEMPS_4][2] PROGMEM = { + {1*4, 430*8},{54*4, 137*8},{107*4, 107*8},{160*4, 91*8},{213*4, 80*8},{266*4, 71*8},{319*4, 64*8},{372*4, 57*8},{425*4, 51*8}, + {478*4, 46*8},{531*4, 41*8},{584*4, 35*8},{637*4, 30*8},{690*4, 25*8},{743*4, 20*8},{796*4, 14*8},{849*4, 7*8},{902*4, 0*8}, + {955*4, -11*8},{1008*4, -35*8}}; +#if NUM_TEMPS_USERTHERMISTOR0>0 +const short temptable_5[NUM_TEMPS_USERTHERMISTOR0][2] PROGMEM = USER_THERMISTORTABLE0 ; +#endif +#if NUM_TEMPS_USERTHERMISTOR1>0 +const short temptable_6[NUM_TEMPS_USERTHERMISTOR1][2] PROGMEM = USER_THERMISTORTABLE1 ; +#endif +#if NUM_TEMPS_USERTHERMISTOR2>0 +const short temptable_7[NUM_TEMPS_USERTHERMISTOR2][2] PROGMEM = USER_THERMISTORTABLE2 ; +#endif +const short * const temptables[7] PROGMEM = {(short int *)&temptable_1[0][0],(short int *)&temptable_2[0][0],(short int *)&temptable_3[0][0],(short int *)&temptable_4[0][0] +#if NUM_TEMPS_USERTHERMISTOR0>0 +,(short int *)&temptable_5[0][0] +#else +,0 +#endif +#if NUM_TEMPS_USERTHERMISTOR1>0 +,(short int *)&temptable_6[0][0] +#else +,0 +#endif +#if NUM_TEMPS_USERTHERMISTOR2>0 +,(short int *)&temptable_7[0][0] +#else +,0 +#endif +}; +const byte temptables_num[7] PROGMEM = {NUMTEMPS_1,NUMTEMPS_2,NUMTEMPS_3,NUMTEMPS_4,NUM_TEMPS_USERTHERMISTOR0,NUM_TEMPS_USERTHERMISTOR1,NUM_TEMPS_USERTHERMISTOR2}; + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- read_raw_temperature ---------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +int read_raw_temperature(byte type,byte pin) { + switch(type) { +#if ANALOG_INPUTS>0 + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 97: + case 98: + case 99: + return (1023<<(2-ANALOG_REDUCE_BITS))-(osAnalogInputValues[pin]>>(ANALOG_REDUCE_BITS)); // Convert to 10 bit result + case 50: // User defined PTC table + case 51: + case 52: + return (osAnalogInputValues[pin]>>(ANALOG_REDUCE_BITS)); // Convert to 10 bit result + case 100: // AD595 + return (osAnalogInputValues[pin]>>(ANALOG_REDUCE_BITS)); +#endif +#ifdef SUPPORT_MAX6675 + case 101: // MAX6675 + return read_max6675(pin); +#endif + } + return 4095; // unknown method, return high value to switch heater off for safety +} + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- conv_raw_temp ----------------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +float conv_raw_temp(byte type,int raw_temp) { + //OUT_P_I_LN("OC for raw ",raw_temp); + switch(type) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + { + type--; + byte num = pgm_read_byte(&temptables_num[type])<<1; + byte i=2; + const short *temptable = (const short *)pgm_read_word(&temptables[type]); //pgm_read_word_near(&temptables[type]); + short oldraw = pgm_read_word(&temptable[0]); + short oldtemp = pgm_read_word(&temptable[1]); + short newraw,newtemp; + raw_temp = (1023<<(2-ANALOG_REDUCE_BITS))-raw_temp; + while(i raw_temp) { + //OUT_P_I("RC O:",oldtemp);OUT_P_I_LN(" OR:",oldraw); + //OUT_P_I("RC N:",newtemp);OUT_P_I_LN(" NR:",newraw); + return TEMP_INT_TO_FLOAT(oldtemp + (float)(raw_temp-oldraw)*(float)(newtemp-oldtemp)/(newraw-oldraw)); + } + oldtemp = newtemp; + oldraw = newraw; + } + // Overflow: Set to last value in the table + return TEMP_INT_TO_FLOAT(newtemp);} + case 50: // User defined PTC thermistor + case 51: + case 52: + { + type-=46; + byte num = pgm_read_byte(&temptables_num[type])<<1; + byte i=2; + const short *temptable = (const short *)pgm_read_word(&temptables[type]); //pgm_read_word_near(&temptables[type]); + short oldraw = pgm_read_word(&temptable[0]); + short oldtemp = pgm_read_word(&temptable[1]); + short newraw,newtemp; + while(i raw_temp) + return TEMP_INT_TO_FLOAT(oldtemp + (float)(raw_temp-oldraw)*(float)(newtemp-oldtemp)/(newraw-oldraw)); + oldtemp = newtemp; + oldraw = newraw; + } + // Overflow: Set to last value in the table + return TEMP_INT_TO_FLOAT(newtemp); + } + case 100: // AD595 + return (int)((long)raw_temp * 500/(1024<<(2-ANALOG_REDUCE_BITS))); +#ifdef SUPPORT_MAX6675 + case 101: // MAX6675 + return raw_temp /4; +#endif +#if defined(USE_GENERIC_THERMISTORTABLE_1) || defined(USE_GENERIC_THERMISTORTABLE_2) || defined(USE_GENERIC_THERMISTORTABLE_3) + case 97: + case 98: + case 99: { + byte i=2; + const short *temptable; +#ifdef USE_GENERIC_THERMISTORTABLE_1 + if(type == 97) + temptable = (const short *)temptable_generic1; +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_2 + if(type == 98) + temptable = (const short *)temptable_generic2; +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_3 + if(type == 99) + temptable = (const short *)temptable_generic3; +#endif + short oldraw = temptable[0]; + short oldtemp = temptable[1]; + short newraw,newtemp; + raw_temp = (1023<<(2-ANALOG_REDUCE_BITS))-raw_temp; + //OUT_P_I("Raw ",raw_temp); + while(i raw_temp) { + //OUT_P_I("RC O:",oldtemp);OUT_P_I_LN(" OR:",oldraw); + //OUT_P_I("RC N:",newtemp);OUT_P_I_LN(" NR:",newraw); + return TEMP_INT_TO_FLOAT(oldtemp + (float)(raw_temp-oldraw)*(float)(newtemp-oldtemp)/(newraw-oldraw)); + } + oldtemp = newtemp; + oldraw = newraw; + } + // Overflow: Set to last value in the table + return TEMP_INT_TO_FLOAT(newtemp); + } +#endif + } +} + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- conv_temp_raw ----------------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +int conv_temp_raw(byte type,float tempf) { + int temp = TEMP_FLOAT_TO_INT(tempf); + switch(type) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + { + type--; + byte num = pgm_read_byte(&temptables_num[type])<<1; + byte i=2; + const short *temptable = (const short *)pgm_read_word(&temptables[type]); //pgm_read_word(&temptables[type]); + short oldraw = pgm_read_word(&temptable[0]); + short oldtemp = pgm_read_word(&temptable[1]); + short newraw,newtemp; + while(i temp) + return oldraw + (long)(oldtemp-temp)*(long)(oldraw-newraw)/(oldtemp-newtemp); + oldtemp = newtemp; + oldraw = newraw; + } + // Overflow: Set to last value in the table + return newraw; + } + case 100: // HEATER_USES_AD595 + return (int)((long)temp * (1024<<(2-ANALOG_REDUCE_BITS))/ 500); +#ifdef SUPPORT_MAX6675 + case 101: // defined HEATER_USES_MAX6675 + return temp * 4; +#endif +#if defined(USE_GENERIC_THERMISTORTABLE_1) || defined(USE_GENERIC_THERMISTORTABLE_2) || defined(USE_GENERIC_THERMISTORTABLE_3) + case 97: + case 98: + case 99: { + byte i=2; + const short *temptable; +#ifdef USE_GENERIC_THERMISTORTABLE_1 + if(type == 97) + temptable = (const short *)temptable_generic1; +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_2 + if(type == 98) + temptable = (const short *)temptable_generic2; +#endif +#ifdef USE_GENERIC_THERMISTORTABLE_3 + if(type == 99) + temptable = (const short *)temptable_generic3; +#endif + short oldraw = temptable[0]; + short oldtemp = temptable[1]; + short newraw,newtemp; + while(itargetTemperature = 0; + c->targetTemperatureC = 0; + pwm_pos[c->pwmIndex] = 0; + } + autotuneIndex = 255; +} +// ------------------------------------------------------------------------------------------------------------------ +// ------------------------------------------------ autotunePID ----------------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +#ifdef TEMP_PID +void autotunePID(float temp,int controllerId) +{ + TemperatureController *c = tempController[controllerId]; + float currentTemp; + int cycles=0; + bool heating = true; + + unsigned long temp_millis = millis(); + unsigned long t1=temp_millis; + unsigned long t2=temp_millis; + long t_high; + long t_low; + + long bias=c->pidMax>>1; + long d = c->pidMax>>1; + float Ku, Tu; + float Kp, Ki, Kd; + float maxTemp=20, minTemp=20; + + OUT_P_LN("PID Autotune start"); + + disableAllHeater(); // switch off all heaters. + autotuneIndex = controllerId; + pwm_pos[c->pwmIndex] = c->pidMax; + + for(;;) { + c->currentTemperature = read_raw_temperature(c->sensorType,c->sensorPin); + currentTemp = c->currentTemperatureC = conv_raw_temp(c->sensorType,c->currentTemperature); + unsigned long time = millis(); + maxTemp=max(maxTemp,currentTemp); + minTemp=min(minTemp,currentTemp); + if(heating == true && currentTemp > temp) { // switch heating -> off + if(time - t2 > (controllerIdpwmIndex] = (bias - d); + t1=time; + t_high=t1 - t2; + maxTemp=temp; + } + } + if(heating == false && currentTemp < temp) { + if(time - t1 > (controllerId 0) { + bias += (d*(t_high - t_low))/(t_low + t_high); + bias = constrain(bias, 20 ,c->pidMax-20); + if(bias > c->pidMax/2) d = c->pidMax - 1 - bias; + else d = bias; + + OUT_P_I(" bias: ",bias); + OUT_P_I(" d: ",d); + OUT_P_F(" min: ",minTemp); + OUT_P_F_LN(" max: ",maxTemp); + if(cycles > 2) { + // Parameter according Ziegler–Nichols method: http://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method + Ku = (4.0*d)/(3.14159*(maxTemp-minTemp)); + Tu = ((float)(t_low + t_high)/1000.0); + OUT_P_F(" Ku: ",Ku); + OUT_P_F_LN(" Tu: ",Tu); + Kp = 0.6*Ku; + Ki = 2*Kp/Tu; + Kd = Kp*Tu*0.125; + OUT_P_LN(" Classic PID"); + OUT_P_F_LN(" Kp: ",Kp); + OUT_P_F_LN(" Ki: ",Ki); + OUT_P_F_LN(" Kd: ",Kd); + /* + Kp = 0.33*Ku; + Ki = Kp/Tu; + Kd = Kp*Tu/3; + OUT_P_LN(" Some overshoot"); + OUT_P_F_LN(" Kp: ",Kp); + OUT_P_F_LN(" Ki: ",Ki); + OUT_P_F_LN(" Kd: ",Kd); + Kp = 0.2*Ku; + Ki = 2*Kp/Tu; + Kd = Kp*Tu/3; + OUT_P_LN(" No overshoot"); + OUT_P_F_LN(" Kp: ",Kp); + OUT_P_F_LN(" Ki: ",Ki); + OUT_P_F_LN(" Kd: ",Kd); + */ + } + } + pwm_pos[c->pwmIndex] = (bias + d); + cycles++; + minTemp=temp; + } + } + if(currentTemp > (temp + 20)) { + OUT_P_LN("PID Autotune failed! Temperature to high"); + disableAllHeater(); + return; + } + if(time - temp_millis > 1000) { + temp_millis = time; + print_temperatures(); + } + if(((time - t1) + (time - t2)) > (10L*60L*1000L*2L)) { // 20 Minutes + OUT_P_LN("PID Autotune failed! timeout"); + disableAllHeater(); + return; + } + if(cycles > 5) { + OUT_P_LN("PID Autotune finished ! Place the Kp, Ki and Kd constants in the configuration.h"); + disableAllHeater(); + return; + } + UI_MEDIUM; + UI_SLOW; + } +} +#endif + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- write_monitor ----------------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +/** \brief Writes monitored temperatures. + +This function is called every 250ms to write the monitored temperature. If monitoring is +disabled, the function is not called. +*/ +void write_monitor() { + out.print_long_P(PSTR("MTEMP:"),millis()); + TemperatureController *act = tempController[manage_monitor]; + OUT_P_F(" ",act->currentTemperatureC); + OUT_P_FX(" ",act->targetTemperatureC,0); + OUT_P_I_LN(" ",pwm_pos[act->pwmIndex]); +} + +// ------------------------------------------------------------------------------------------------------------------ +// --------------------------------------------- reportTempsensorError ---------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +bool reportTempsensorError() { + if(!(printer_state.flag0 & PRINTER_FLAG0_TEMPSENSOR_DEFECT)) return false; + for(byte i=0;icurrentTemperatureC; + if(i==NUM_EXTRUDER) OUT_P("heated bed"); + else OUT_P_I("extruder ",i); + if(temp<-10 || temp>400) { + OUT_P_LN(": temp sensor defect"); + } else OUT_P_LN(": working"); + } + OUT_P_LN("Printer set into dry run mode until restart!"); + return true; +} + +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- manage temperatures ----------------------------------------------- +// ------------------------------------------------------------------------------------------------------------------ + +/** Makes updates to temperatures and heater state every call. + +Is called every 100ms. +*/ + +void manage_temperatures() { + for(byte controller=0;controllercurrentTemperatureC; + act->currentTemperature = read_raw_temperature(act->sensorType,act->sensorPin); + act->currentTemperatureC = conv_raw_temp(act->sensorType,act->currentTemperature); + if(!(printer_state.flag0 & PRINTER_FLAG0_TEMPSENSOR_DEFECT) && (act->currentTemperatureC<-10 || act->currentTemperatureC>400)) { // no temp sensor or short in sensor, disable heater + printer_state.flag0 |= PRINTER_FLAG0_TEMPSENSOR_DEFECT; + reportTempsensorError(); + } + if(printer_state.flag0 & PRINTER_FLAG0_TEMPSENSOR_DEFECT) continue; + byte on = act->currentTemperature>=act->targetTemperature ? LOW : HIGH; +#ifdef TEMP_PID + act->tempArray[act->tempPointer++] = act->currentTemperatureC; + act->tempPointer &= 3; + if(act->heatManager==1) { + byte output; + float error = act->targetTemperatureC - act->currentTemperatureC; + if(act->targetTemperatureC<20.0f) output = 0; // off is off, even if damping term wants a heat peak! + else if(error>PID_CONTROL_RANGE) + output = act->pidMax; + else if(error<-PID_CONTROL_RANGE) + output = 0; + else { + float pidTerm = act->pidPGain * error; + act->tempIState = constrain(act->tempIState+error,act->tempIStateLimitMin,act->tempIStateLimitMax); + pidTerm += act->pidIGain * act->tempIState*0.1; + long dgain = act->pidDGain * (act->tempArray[act->tempPointer]-act->currentTemperatureC)*3.333f; + pidTerm += dgain; +#if SCALE_PID_TO_MAX==1 + pidTerm = (pidTerm*act->pidMax)*0.0039062; +#endif + output = constrain((int)pidTerm, 0, act->pidMax); + } + pwm_pos[act->pwmIndex] = output; + } else +#endif + if(act->heatManager == 2) { // Bang-bang with reduced change frequency to save relais life + unsigned long time = millis(); + if (time - act->lastTemperatureUpdate > HEATED_BED_SET_INTERVAL) { + pwm_pos[act->pwmIndex] = (on ? 255 : 0); + act->lastTemperatureUpdate = time; + } + } else { // Fast Bang-Bang fallback + pwm_pos[act->pwmIndex] = (on ? 255 : 0); + } +#ifdef MAXTEMP + if(act->currentTemperatureC>MAXTEMP) // Force heater off if MAXTEMP is exceeded + pwm_pos[act->pwmIndex] = 0; +#endif +#if LED_PIN>-1 + if(act == ¤t_extruder->tempControl) + WRITE(LED_PIN,on); +#endif + } + if(printer_state.flag0 & PRINTER_FLAG0_TEMPSENSOR_DEFECT) { + for(byte i=0;ipwmIndex] = 0; + } + debug_level |= 8; // Go into dry mode + } + +} +// ------------------------------------------------------------------------------------------------------------------ +// ---------------------------------------------- read_max6675 ------------------------------------------------------ +// ------------------------------------------------------------------------------------------------------------------ + +#ifdef SUPPORT_MAX6675 +int read_max6675(byte ss_pin) +{ + int max6675_temp = 0; + #ifdef PRR + PRR &= ~(1<> 3; // thermocouple open? +} +#endif + diff --git a/Repetier/FatStructs.h b/Repetier/FatStructs.h new file mode 100644 index 0000000..5713467 --- /dev/null +++ b/Repetier/FatStructs.h @@ -0,0 +1,418 @@ +/* Arduino SdFat Library + * Copyright (C) 2009 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +#ifndef FatStructs_h +#define FatStructs_h +/** + * \file + * FAT file structures + */ +/* + * mostly from Microsoft document fatgen103.doc + * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + */ +//------------------------------------------------------------------------------ +/** Value for byte 510 of boot block or MBR */ +uint8_t const BOOTSIG0 = 0X55; +/** Value for byte 511 of boot block or MBR */ +uint8_t const BOOTSIG1 = 0XAA; +//------------------------------------------------------------------------------ +/** + * \struct partitionTable + * \brief MBR partition table entry + * + * A partition table entry for a MBR formatted storage device. + * The MBR partition table has four entries. + */ +struct partitionTable { + /** + * Boot Indicator . Indicates whether the volume is the active + * partition. Legal values include: 0X00. Do not use for booting. + * 0X80 Active partition. + */ + uint8_t boot; + /** + * Head part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t beginHead; + /** + * Sector part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned beginSector : 6; + /** High bits cylinder for first block in partition. */ + unsigned beginCylinderHigh : 2; + /** + * Combine beginCylinderLow with beginCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t beginCylinderLow; + /** + * Partition type. See defines that begin with PART_TYPE_ for + * some Microsoft partition types. + */ + uint8_t type; + /** + * head part of cylinder-head-sector address of the last sector in the + * partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t endHead; + /** + * Sector part of cylinder-head-sector address of the last sector in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned endSector : 6; + /** High bits of end cylinder */ + unsigned endCylinderHigh : 2; + /** + * Combine endCylinderLow with endCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t endCylinderLow; + /** Logical block address of the first block in the partition. */ + uint32_t firstSector; + /** Length of the partition, in blocks. */ + uint32_t totalSectors; +}; +/** Type name for partitionTable */ +typedef struct partitionTable part_t; +//------------------------------------------------------------------------------ +/** + * \struct masterBootRecord + * + * \brief Master Boot Record + * + * The first block of a storage device that is formatted with a MBR. + */ +struct masterBootRecord { + /** Code Area for master boot program. */ + uint8_t codeArea[440]; + /** Optional WindowsNT disk signature. May contain more boot code. */ + uint32_t diskSignature; + /** Usually zero but may be more boot code. */ + uint16_t usuallyZero; + /** Partition tables. */ + part_t part[4]; + /** First MBR signature byte. Must be 0X55 */ + uint8_t mbrSig0; + /** Second MBR signature byte. Must be 0XAA */ + uint8_t mbrSig1; +}; +/** Type name for masterBootRecord */ +typedef struct masterBootRecord mbr_t; +//------------------------------------------------------------------------------ +/** + * \struct biosParmBlock + * + * \brief BIOS parameter block + * + * The BIOS parameter block describes the physical layout of a FAT volume. + */ +struct biosParmBlock { + /** + * Count of bytes per sector. This value may take on only the + * following values: 512, 1024, 2048 or 4096 + */ + uint16_t bytesPerSector; + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. + */ + uint8_t sectorsPerCluster; + /** + * Number of sectors before the first FAT. + * This value must not be zero. + */ + uint16_t reservedSectorCount; + /** The count of FAT data structures on the volume. This field should + * always contain the value 2 for any FAT volume of any type. + */ + uint8_t fatCount; + /** + * For FAT12 and FAT16 volumes, this field contains the count of + * 32-byte directory entries in the root directory. For FAT32 volumes, + * this field must be set to 0. For FAT12 and FAT16 volumes, this + * value should always specify a count that when multiplied by 32 + * results in a multiple of bytesPerSector. FAT16 volumes should + * use the value 512. + */ + uint16_t rootDirEntryCount; + /** + * This field is the old 16-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then totalSectors32 + * must be non-zero. For FAT32 volumes, this field must be 0. For + * FAT12 and FAT16 volumes, this field contains the sector count, and + * totalSectors32 is 0 if the total sector count fits + * (is less than 0x10000). + */ + uint16_t totalSectors16; + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (non-removable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ + uint8_t mediaType; + /** + * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrtack; + /** Number of heads for interrupt 0x13. Not used otherwise. */ + uint16_t headCount; + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ + uint32_t hidddenSectors; + /** + * This field is the new 32-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then + * totalSectors16 must be non-zero. + */ + uint32_t totalSectors32; + /** + * Count of sectors occupied by one FAT on FAT32 volumes. + */ + uint32_t sectorsPerFat32; + /** + * This field is only defined for FAT32 media and does not exist on + * FAT12 and FAT16 media. + * Bits 0-3 -- Zero-based number of active FAT. + * Only valid if mirroring is disabled. + * Bits 4-6 -- Reserved. + * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. + * -- 1 means only one FAT is active; it is the one referenced in bits 0-3. + * Bits 8-15 -- Reserved. + */ + uint16_t fat32Flags; + /** + * FAT32 version. High byte is major revision number. + * Low byte is minor revision number. Only 0.0 define. + */ + uint16_t fat32Version; + /** + * Cluster number of the first cluster of the root directory for FAT32. + * This usually 2 but not required to be 2. + */ + uint32_t fat32RootCluster; + /** + * Sector number of FSINFO structure in the reserved area of the + * FAT32 volume. Usually 1. + */ + uint16_t fat32FSInfo; + /** + * If non-zero, indicates the sector number in the reserved area + * of the volume of a copy of the boot record. Usually 6. + * No value other than 6 is recommended. + */ + uint16_t fat32BackBootBlock; + /** + * Reserved for future expansion. Code that formats FAT32 volumes + * should always set all of the bytes of this field to 0. + */ + uint8_t fat32Reserved[12]; +}; +/** Type name for biosParmBlock */ +typedef struct biosParmBlock bpb_t; +//------------------------------------------------------------------------------ +/** + * \struct fat32BootSector + * + * \brief Boot sector for a FAT16 or FAT32 volume. + * + */ +struct fat32BootSector { + /** X86 jmp to boot program */ + uint8_t jmpToBootCode[3]; + /** informational only - don't depend on it */ + char oemName[8]; + /** BIOS Parameter Block */ + bpb_t bpb; + /** for int0x13 use value 0X80 for hard drive */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 0X29 if next three fields are valid */ + uint8_t bootSignature; + /** usually generated by combining date and time */ + uint32_t volumeSerialNumber; + /** should match volume label in root dir */ + char volumeLabel[11]; + /** informational only - don't depend on it */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[420]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +}; +//------------------------------------------------------------------------------ +// End Of Chain values for FAT entries +/** FAT16 end of chain value used by Microsoft. */ +uint16_t const FAT16EOC = 0XFFFF; +/** Minimum value for FAT16 EOC. Use to test for EOC. */ +uint16_t const FAT16EOC_MIN = 0XFFF8; +/** FAT32 end of chain value used by Microsoft. */ +uint32_t const FAT32EOC = 0X0FFFFFFF; +/** Minimum value for FAT32 EOC. Use to test for EOC. */ +uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; +/** Mask a for FAT32 entry. Entries are 28 bits. */ +uint32_t const FAT32MASK = 0X0FFFFFFF; + +/** Type name for fat32BootSector */ +typedef struct fat32BootSector fbs_t; +//------------------------------------------------------------------------------ +/** + * \struct directoryEntry + * \brief FAT short directory entry + * + * Short means short 8.3 name, not the entry size. + * + * Date Format. A FAT directory entry date stamp is a 16-bit field that is + * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the + * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the + * 16-bit word): + * + * Bits 9-15: Count of years from 1980, valid value range 0-127 + * inclusive (1980-2107). + * + * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. + * + * Bits 0-4: Day of month, valid value range 1-31 inclusive. + * + * Time Format. A FAT directory entry time stamp is a 16-bit field that has + * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the + * 16-bit word, bit 15 is the MSB of the 16-bit word). + * + * Bits 11-15: Hours, valid value range 0-23 inclusive. + * + * Bits 5-10: Minutes, valid value range 0-59 inclusive. + * + * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). + * + * The valid time range is from Midnight 00:00:00 to 23:59:58. + */ +struct directoryEntry { + /** + * Short 8.3 name. + * The first eight bytes contain the file name with blank fill. + * The last three bytes contain the file extension with blank fill. + */ + uint8_t name[11]; + /** Entry attributes. + * + * The upper two bits of the attribute byte are reserved and should + * always be set to 0 when a file is created and never modified or + * looked at after that. See defines that begin with DIR_ATT_. + */ + uint8_t attributes; + /** + * Reserved for use by Windows NT. Set value to 0 when a file is + * created and never modify or look at it after that. + */ + uint8_t reservedNT; + /** + * The granularity of the seconds part of creationTime is 2 seconds + * so this field is a count of tenths of a second and its valid + * value range is 0-199 inclusive. (WHG note - seems to be hundredths) + */ + uint8_t creationTimeTenths; + /** Time file was created. */ + uint16_t creationTime; + /** Date file was created. */ + uint16_t creationDate; + /** + * Last access date. Note that there is no last access time, only + * a date. This is the date of last read or write. In the case of + * a write, this should be set to the same date as lastWriteDate. + */ + uint16_t lastAccessDate; + /** + * High word of this entry's first cluster number (always 0 for a + * FAT12 or FAT16 volume). + */ + uint16_t firstClusterHigh; + /** Time of last write. File creation is considered a write. */ + uint16_t lastWriteTime; + /** Date of last write. File creation is considered a write. */ + uint16_t lastWriteDate; + /** Low word of this entry's first cluster number. */ + uint16_t firstClusterLow; + /** 32-bit unsigned holding this file's size in bytes. */ + uint32_t fileSize; +}; +//------------------------------------------------------------------------------ +// Definitions for directory entries +// +/** Type name for directoryEntry */ +typedef struct directoryEntry dir_t; +/** escape for name[0] = 0XE5 */ +uint8_t const DIR_NAME_0XE5 = 0X05; +/** name[0] value for entry that is free after being "deleted" */ +uint8_t const DIR_NAME_DELETED = 0XE5; +/** name[0] value for entry that is free and no allocated entries follow */ +uint8_t const DIR_NAME_FREE = 0X00; +/** file is read-only */ +uint8_t const DIR_ATT_READ_ONLY = 0X01; +/** File should hidden in directory listings */ +uint8_t const DIR_ATT_HIDDEN = 0X02; +/** Entry is for a system file */ +uint8_t const DIR_ATT_SYSTEM = 0X04; +/** Directory entry contains the volume label */ +uint8_t const DIR_ATT_VOLUME_ID = 0X08; +/** Entry is for a directory */ +uint8_t const DIR_ATT_DIRECTORY = 0X10; +/** Old DOS archive bit for backup support */ +uint8_t const DIR_ATT_ARCHIVE = 0X20; +/** Test value for long name entry. Test is + (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ +uint8_t const DIR_ATT_LONG_NAME = 0X0F; +/** Test mask for long name entry */ +uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; +/** defined attribute bits */ +uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; +/** Directory entry is part of a long name */ +static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { + return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; +} +/** Mask for file/subdirectory tests */ +uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); +/** Directory entry is for a file */ +static inline uint8_t DIR_IS_FILE(const dir_t* dir) { + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; +} +/** Directory entry is for a subdirectory */ +static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; +} +/** Directory entry is for a file or subdirectory */ +static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { + return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; +} +#endif // FatStructs_h diff --git a/Repetier/Repetier.ino b/Repetier/Repetier.ino new file mode 100644 index 0000000..0706cc6 --- /dev/null +++ b/Repetier/Repetier.ino @@ -0,0 +1,2674 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. + + Main author: repetier + +*/ +/** +\mainpage Repetier-Firmware for Arduino based RepRaps +
Copyright © 2011-2013 by repetier +
+ +\section Intro Introduction + + +\section GCodes Implemented GCodes + + look here for descriptions of gcodes: http://linuxcnc.org/handbook/gcode/g-code.html + and http://objects.reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes + +Implemented Codes + +- G0 -> G1 +- G1 - Coordinated Movement X Y Z E +- G4 - Dwell S or P +- G20 - Units for G0/G1 are inches. +- G21 - Units for G0/G1 are mm. +- G28 - Home all axis or named axis. +- G90 - Use absolute coordinates +- G91 - Use relative coordinates +- G92 - Set current position to cordinates given + +RepRap M Codes + +- M104 - Set extruder target temp +- M105 - Read current temp +- M106 - Fan on +- M107 - Fan off +- M109 - Wait for extruder current temp to reach target temp. +- M114 - Display current position + +Custom M Codes + +- M80 - Turn on Power Supply +- M20 - List SD card +- M21 - Init SD card +- M22 - Release SD card +- M23 - Select SD file (M23 filename.g) +- M24 - Start/resume SD print +- M25 - Pause SD print +- M26 - Set SD position in bytes (M26 S12345) +- M27 - Report SD print status +- M28 - Start SD write (M28 filename.g) +- M29 - Stop SD write +- M30 - Delete file on sd card +- M32 create subdirectory +- M42 P S - Change output of pin P to S. Does not work on most important pins. +- M80 - Turn on power supply +- M81 - Turn off power supply +- M82 - Set E codes absolute (default) +- M83 - Set E codes relative while in Absolute Coordinates (G90) mode +- M84 - Disable steppers until next move, + or use S to specify an inactivity timeout, after which the steppers will be disabled. S0 to disable the timeout. +- M85 - Set inactivity shutdown timer with parameter S. To disable set zero (default) +- M92 - Set axis_steps_per_unit - same syntax as G92 +- M112 - Emergency kill +- M115- Capabilities string +- M117 - Write message in status row on lcd +- M119 - Report endstop status +- M140 - Set bed target temp +- M190 - Wait for bed current temp to reach target temp. +- M201 - Set max acceleration in units/s^2 for print moves (M201 X1000 Y1000) +- M202 - Set max acceleration in units/s^2 for travel moves (M202 X1000 Y1000) +- M203 - Set temperture monitor to Sx +- M204 - Set PID parameter X => Kp Y => Ki Z => Kd S Default is current extruder. NUM_EXTRUDER=Heated bed +- M205 - Output EEPROM settings +- M206 - Set EEPROM value +- M220 S - Increase/decrease given feedrate +- M221 S - Increase/decrease given flow rate +- M231 S X Y Z F - Set OPS parameter +- M232 - Read and reset max. advance values +- M233 X Y - Set temporary advance K-value to X and linear term advanceL to Y +- M251 Measure Z steps from homing stop (Delta printers). S0 - Reset, S1 - Print, S2 - Store to Z length (also EEPROM if enabled) +- M303 P S Autodetect pid values. Use P for heated bed. +- M350 S X Y Z E P : Set microstepping on RAMBO board +- M400 - Wait until move buffers empty. +- M401 - Store x, y and z position. +- M402 - Go to stored position. If X, Y or Z is specified, only these coordinates are used. F changes feedrate fo rthat move. +- M500 Store settings to EEPROM +- M501 Load settings from EEPROM +- M502 Reset settings to the one in configuration.h. Does not store values in EEPROM! +- M908 P
S : Set stepper current for digipot (RAMBO board) +*/ + +#include "Reptier.h" +#include "Eeprom.h" +#include "pins_arduino.h" +#include "fastio.h" +#include "ui.h" +#include +#include + +#if UI_DISPLAY_TYPE==4 +//#include // Uncomment this if you are using liquid crystal library +#endif + +// ================ Sanity checks ================ +#ifndef STEP_DOUBLER_FREQUENCY +#error Please add new parameter STEP_DOUBLER_FREQUENCY to your configuration. +#else +#if STEP_DOUBLER_FREQUENCY<10000 || STEP_DOUBLER_FREQUENCY>20000 +#error STEP_DOUBLER_FREQUENCY should be in range 10000-16000. +#endif +#endif +#ifdef EXTRUDER_SPEED +#error EXTRUDER_SPEED is not used any more. Values are now taken from extruder definition. +#endif +#if MAX_HALFSTEP_INTERVAL<=1900 +#error MAX_HALFSTEP_INTERVAL must be greater then 1900 +#endif +#ifdef ENDSTOPPULLUPS +#error ENDSTOPPULLUPS is now replaced by individual pullup configuration! +#endif +#ifdef EXT0_PID_PGAIN +#error The PID system has changed. Please use the new float number options! +#endif +// #################################################################################### +// # No configuration below this line - just some errorchecking # +// #################################################################################### +#ifdef SUPPORT_MAX6675 +#if !defined SCK_PIN || !defined MOSI_PIN || !defined MISO_PIN +#error For MAX6675 support, you need to define SCK_PIN, MISO_PIN and MOSI_PIN in pins.h +#endif +#endif +#if X_STEP_PIN<0 || Y_STEP_PIN<0 || Z_STEP_PIN<0 +#error One of the following pins is not assigned: X_STEP_PIN,Y_STEP_PIN,Z_STEP_PIN +#endif +#if EXT0_STEP_PIN<0 && NUM_EXTRUDER>0 +#error EXT0_STEP_PIN not set to a pin number. +#endif +#if EXT0_DIR_PIN<0 && NUM_EXTRUDER>0 +#error EXT0_DIR_PIN not set to a pin number. +#endif +#if MOVE_CACHE_SIZE<4 +#error MOVE_CACHE_SIZE must be at least 5 +#endif + +#if DRIVE_SYSTEM==3 +#define SIN_60 0.8660254037844386 +#define COS_60 0.5 +#define DELTA_DIAGONAL_ROD_STEPS (AXIS_STEPS_PER_MM * DELTA_DIAGONAL_ROD) +#define DELTA_DIAGONAL_ROD_STEPS_SQUARED (DELTA_DIAGONAL_ROD_STEPS * DELTA_DIAGONAL_ROD_STEPS) +#define DELTA_ZERO_OFFSET_STEPS (AXIS_STEPS_PER_MM * DELTA_ZERO_OFFSET) +#define DELTA_RADIUS_STEPS (AXIS_STEPS_PER_MM * DELTA_RADIUS) + +#define DELTA_TOWER1_X_STEPS -SIN_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER1_Y_STEPS -COS_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER2_X_STEPS SIN_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER2_Y_STEPS -COS_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER3_X_STEPS 0.0 +#define DELTA_TOWER3_Y_STEPS DELTA_RADIUS_STEPS + +#define NUM_AXIS 4 +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 +#define E_AXIS 3 + +#endif + +#define OVERFLOW_PERIODICAL (int)(F_CPU/(TIMER0_PRESCALE*40)) +// RAM usage of variables: Non RAMPS 114+MOVE_CACHE_SIZE*59+printer_state(32) = 382 Byte with MOVE_CACHE_SIZE=4 +// RAM usage RAMPS adds: 96 +// RAM usage SD Card: +byte unit_inches = 0; ///< 0 = Units are mm, 1 = units are inches. +//Stepper Movement Variables +float axis_steps_per_unit[4] = {XAXIS_STEPS_PER_MM,YAXIS_STEPS_PER_MM,ZAXIS_STEPS_PER_MM,1}; ///< Number of steps per mm needed. +float inv_axis_steps_per_unit[4]; ///< Inverse of axis_steps_per_unit for faster conversion +float max_feedrate[4] = {MAX_FEEDRATE_X, MAX_FEEDRATE_Y, MAX_FEEDRATE_Z}; ///< Maximum allowed feedrate. +float homing_feedrate[3] = {HOMING_FEEDRATE_X, HOMING_FEEDRATE_Y, HOMING_FEEDRATE_Z}; +byte STEP_PIN[3] = {X_STEP_PIN, Y_STEP_PIN, Z_STEP_PIN}; +#ifdef RAMP_ACCELERATION +// float max_start_speed_units_per_second[4] = MAX_START_SPEED_UNITS_PER_SECOND; ///< Speed we can use, without acceleration. + long max_acceleration_units_per_sq_second[4] = {MAX_ACCELERATION_UNITS_PER_SQ_SECOND_X,MAX_ACCELERATION_UNITS_PER_SQ_SECOND_Y,MAX_ACCELERATION_UNITS_PER_SQ_SECOND_Z}; ///< X, Y, Z and E max acceleration in mm/s^2 for printing moves or retracts + long max_travel_acceleration_units_per_sq_second[4] = {MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_X,MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_Y,MAX_TRAVEL_ACCELERATION_UNITS_PER_SQ_SECOND_Z}; ///< X, Y, Z max acceleration in mm/s^2 for travel moves + /** Acceleration in steps/s^3 in printing mode.*/ + unsigned long axis_steps_per_sqr_second[4]; + /** Acceleration in steps/s^2 in movement mode.*/ + unsigned long axis_travel_steps_per_sqr_second[4]; +#endif +#if DRIVE_SYSTEM==3 +DeltaSegment segments[DELTA_CACHE_SIZE]; +unsigned int delta_segment_write_pos = 0; // Position where we write the next cached delta move +volatile unsigned int delta_segment_count = 0; // Number of delta moves cached 0 = nothing in cache +byte lastMoveID = 0; // Last move ID +#endif +PrinterState printer_state; +byte relative_mode = false; ///< Determines absolute (false) or relative Coordinates (true). +byte relative_mode_e = false; ///< Determines Absolute or Relative E Codes while in Absolute Coordinates mode. E is always relative in Relative Coordinates mode. +byte debug_level = 6; ///< Bitfield defining debug output. 1 = echo, 2 = info, 4 = error, 8 = dry run., 16 = Only communication, 32 = No moves + +//Inactivity shutdown variables +unsigned long previous_millis_cmd = 0; +unsigned long max_inactive_time = MAX_INACTIVE_TIME*1000L; +unsigned long stepper_inactive_time = STEPPER_INACTIVE_TIME*1000L; +PrintLine lines[MOVE_CACHE_SIZE]; ///< Cache for print moves. +PrintLine *cur = 0; ///< Current printing line +byte lines_write_pos=0; ///< Position where we write the next cached line move. +volatile byte lines_count=0; ///< Number of lines cached 0 = nothing to do. +byte lines_pos=0; ///< Position for executing line movement. +long baudrate = BAUDRATE; ///< Communication speed rate. +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE +int maxadv=0; +#endif +int maxadv2=0; +float maxadvspeed=0; +#endif +byte pwm_pos[NUM_EXTRUDER+3]; // 0-NUM_EXTRUDER = Heater 0-NUM_EXTRUDER of extruder, NUM_EXTRUDER = Heated bed, NUM_EXTRUDER+1 Board fan, NUM_EXTRUDER+2 = Fan + +int waitRelax=0; // Delay filament relax at the end of print, could be a simple timeout +#ifdef USE_OPS +byte printmoveSeen=0; +#endif +#ifdef DEBUG_FREE_MEMORY +int lowest_ram=16384; +int lowest_send=16384; + +void check_mem() { +BEGIN_INTERRUPT_PROTECTED + uint8_t * heapptr, * stackptr; + heapptr = (uint8_t *)malloc(4); // get heap pointer + free(heapptr); // free up the memory again (sets heapptr to 0) + stackptr = (uint8_t *)(SP); // save value of stack pointer + int newfree = (int)stackptr-(int)heapptr; + if(newfreelowest_ram) { + lowest_send = lowest_ram; + out.println_int_P(PSTR("Free RAM:"),lowest_ram); + } +} +#endif + + +void update_extruder_flags() { + printer_state.flag0 &= ~PRINTER_FLAG0_SEPERATE_EXTRUDER_INT; +#if USE_OPS==1 + if(printer_state.opsMode!=0) { + printer_state.flag0 |= PRINTER_FLAG0_SEPERATE_EXTRUDER_INT; + } +#endif +#if defined(USE_ADVANCE) + for(byte i=0;ipin assignments +#if ANALYZER_CH0>=0 +SET_OUTPUT(ANALYZER_CH0); +#endif +#if ANALYZER_CH1>=0 +SET_OUTPUT(ANALYZER_CH1); +#endif +#if ANALYZER_CH2>=0 +SET_OUTPUT(ANALYZER_CH2); +#endif +#if ANALYZER_CH3>=0 +SET_OUTPUT(ANALYZER_CH3); +#endif +#if ANALYZER_CH4>=0 +SET_OUTPUT(ANALYZER_CH4); +#endif +#if ANALYZER_CH5>=0 +SET_OUTPUT(ANALYZER_CH5); +#endif +#if ANALYZER_CH6>=0 +SET_OUTPUT(ANALYZER_CH6); +#endif +#if ANALYZER_CH7>=0 +SET_OUTPUT(ANALYZER_CH7); +#endif +#endif + +#if defined(ENABLE_POWER_ON_STARTUP) && PS_ON_PIN>-1 + SET_OUTPUT(PS_ON_PIN); //GND + WRITE(PS_ON_PIN, LOW); +#endif + + //Initialize Step Pins + SET_OUTPUT(X_STEP_PIN); + SET_OUTPUT(Y_STEP_PIN); + SET_OUTPUT(Z_STEP_PIN); + + + //Initialize Dir Pins +#if X_DIR_PIN>-1 + SET_OUTPUT(X_DIR_PIN); +#endif +#if Y_DIR_PIN>-1 + SET_OUTPUT(Y_DIR_PIN); +#endif +#if Z_DIR_PIN>-1 + SET_OUTPUT(Z_DIR_PIN); +#endif + + //Steppers default to disabled. +#if X_ENABLE_PIN > -1 + if(!X_ENABLE_ON) WRITE(X_ENABLE_PIN,HIGH); + SET_OUTPUT(X_ENABLE_PIN); +#endif +#if Y_ENABLE_PIN > -1 + if(!Y_ENABLE_ON) WRITE(Y_ENABLE_PIN,HIGH); + SET_OUTPUT(Y_ENABLE_PIN); +#endif +#if Z_ENABLE_PIN > -1 + if(!Z_ENABLE_ON) WRITE(Z_ENABLE_PIN,HIGH); + SET_OUTPUT(Z_ENABLE_PIN); +#endif + + //endstop pullups +#if X_MIN_PIN>-1 && MIN_HARDWARE_ENDSTOP_X + SET_INPUT(X_MIN_PIN); +#if ENDSTOP_PULLUP_X_MIN + WRITE(X_MIN_PIN,HIGH); +#endif +#endif +#if Y_MIN_PIN>-1 && MIN_HARDWARE_ENDSTOP_Y + SET_INPUT(Y_MIN_PIN); +#if ENDSTOP_PULLUP_Y_MIN + WRITE(Y_MIN_PIN,HIGH); +#endif +#endif +#if Z_MIN_PIN>-1 && MIN_HARDWARE_ENDSTOP_Z + SET_INPUT(Z_MIN_PIN); +#if ENDSTOP_PULLUP_Z_MIN + WRITE(Z_MIN_PIN,HIGH); +#endif +#endif +#if X_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_X + SET_INPUT(X_MAX_PIN); +#if ENDSTOP_PULLUP_X_MAX + WRITE(X_MAX_PIN,HIGH); +#endif +#endif +#if Y_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_Y + SET_INPUT(Y_MAX_PIN); +#if ENDSTOP_PULLUP_Y_MAX + WRITE(Y_MAX_PIN,HIGH); +#endif +#endif +#if Z_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_Z + SET_INPUT(Z_MAX_PIN); +#if ENDSTOP_PULLUP_Z_MAX + WRITE(Z_MAX_PIN,HIGH); +#endif +#endif +#if FAN_PIN>-1 + SET_OUTPUT(FAN_PIN); + WRITE(FAN_PIN,LOW); +#endif +#if FAN_BOARD_PIN>-1 + SET_OUTPUT(FAN_BOARD_PIN); + WRITE(FAN_BOARD_PIN,LOW); +#endif +#if EXT0_HEATER_PIN>-1 + SET_OUTPUT(EXT0_HEATER_PIN); + WRITE(EXT0_HEATER_PIN,LOW); +#endif +#if defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN>-1 + SET_OUTPUT(EXT1_HEATER_PIN); + WRITE(EXT1_HEATER_PIN,LOW); +#endif +#if defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN>-1 + SET_OUTPUT(EXT2_HEATER_PIN); + WRITE(EXT2_HEATER_PIN,LOW); +#endif +#if defined(EXT3_HEATER_PIN) && EXT3_HEATER_PIN>-1 + SET_OUTPUT(EXT3_HEATER_PIN); + WRITE(EXT3_HEATER_PIN,LOW); +#endif +#if defined(EXT4_HEATER_PIN) && EXT4_HEATER_PIN>-1 + SET_OUTPUT(EXT4_HEATER_PIN); + WRITE(EXT4_HEATER_PIN,LOW); +#endif +#if defined(EXT5_HEATER_PIN) && EXT5_HEATER_PIN>-1 + SET_OUTPUT(EXT5_HEATER_PIN); + WRITE(EXT5_HEATER_PIN,LOW); +#endif + +#ifdef XY_GANTRY + printer_state.motorX = 0; + printer_state.motorY = 0; +#endif + +#if STEPPER_CURRENT_CONTROL!=CURRENT_CONTROL_MANUAL + current_control_init(); // Set current if it is firmware controlled +#endif + microstep_init(); + +#if USE_OPS==1 + printer_state.opsMode = OPS_MODE; + printer_state.opsMinDistance = OPS_MIN_DISTANCE; + printer_state.opsRetractDistance = OPS_RETRACT_DISTANCE; + printer_state.opsRetractBacklash = OPS_RETRACT_BACKLASH; + printer_state.filamentRetracted = false; +#endif + printer_state.feedrate = 50; ///< Current feedrate in mm/s. + printer_state.feedrateMultiply = 100; + printer_state.extrudeMultiply = 100; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + printer_state.advance_executed = 0; +#endif + printer_state.advance_steps_set = 0; + printer_state.advance_lin_set = 0; +#endif + for(byte i=0;i max_inactive_time ) kill(false); + if(stepper_inactive_time!=0 && (curtime-previous_millis_cmd) > stepper_inactive_time ) { kill(true); } +#if defined(SDCARDDETECT) && SDCARDDETECT>-1 && defined(SDSUPPORT) && SDSUPPORT + sd.automount(); +#endif + //void finishNextSegment(); + DEBUG_MEMORY; +} +/** + Main processing loop. It checks perodically for new commands, checks temperatures + and executes new incoming commands. +*/ +void loop() +{ + gcode_read_serial(); + GCode *code = gcode_next_command(); + //UI_SLOW; // do longer timed user interface action + UI_MEDIUM; // do check encoder + if(code){ +#if SDSUPPORT + if(sd.savetosd){ + if(!(GCODE_HAS_M(code) && code->M==29)) { // still writing to file + sd.write_command(code); + } else { + sd.finishWrite(); + } +#ifdef ECHO_ON_EXECUTE + if(DEBUG_ECHO) { + OUT_P("Echo:"); + gcode_print_command(code); + out.println(); + } +#endif + gcode_command_finished(code); + } else { + process_command(code,true); + } +#else + process_command(code,true); +#endif + } + defaultLoopActions(); +} + + +/** \brief Optimized division + +Normally the C compiler will compute a long/long division, which takes ~670 Ticks. +This version is optimized for a 16 bit dividend and recognises the special cases +of a 24 bit and 16 bit dividend, which offen, but not always occur in updating the +interval. +*/ +inline long Div4U2U(unsigned long a,unsigned int b) { +#if CPU_ARCH==ARCH_AVR + // r14/r15 remainder + // r16 counter + __asm__ __volatile__ ( + "clr r14 \n\t" + "sub r15,r15 \n\t" + "tst %D0 \n\t" + "brne do32%= \n\t" + "tst %C0 \n\t" + "breq donot24%= \n\t" + "rjmp do24%= \n\t" + "donot24%=:" "ldi r16,17 \n\t" // 16 Bit divide + "d16u_1%=:" "rol %A0 \n\t" + "rol %B0 \n\t" + "dec r16 \n\t" + "brne d16u_2%= \n\t" + "rjmp end%= \n\t" + "d16u_2%=:" "rol r14 \n\t" + "rol r15 \n\t" + "sub r14,%A2 \n\t" + "sbc r15,%B2 \n\t" + "brcc d16u_3%= \n\t" + "add r14,%A2 \n\t" + "adc r15,%B2 \n\t" + "clc \n\t" + "rjmp d16u_1%= \n\t" + "d16u_3%=:" "sec \n\t" + "rjmp d16u_1%= \n\t" + "do32%=:" // divide full 32 bit + "rjmp do32B%= \n\t" + "do24%=:" // divide 24 bit + + "ldi r16,25 \n\t" // 24 Bit divide + "d24u_1%=:" "rol %A0 \n\t" + "rol %B0 \n\t" + "rol %C0 \n\t" + "dec r16 \n\t" + "brne d24u_2%= \n\t" + "rjmp end%= \n\t" + "d24u_2%=:" "rol r14 \n\t" + "rol r15 \n\t" + "sub r14,%A2 \n\t" + "sbc r15,%B2 \n\t" + "brcc d24u_3%= \n\t" + "add r14,%A2 \n\t" + "adc r15,%B2 \n\t" + "clc \n\t" + "rjmp d24u_1%= \n\t" + "d24u_3%=:" "sec \n\t" + "rjmp d24u_1%= \n\t" + + "do32B%=:" // divide full 32 bit + + "ldi r16,33 \n\t" // 32 Bit divide + "d32u_1%=:" "rol %A0 \n\t" + "rol %B0 \n\t" + "rol %C0 \n\t" + "rol %D0 \n\t" + "dec r16 \n\t" + "brne d32u_2%= \n\t" + "rjmp end%= \n\t" + "d32u_2%=:" "rol r14 \n\t" + "rol r15 \n\t" + "sub r14,%A2 \n\t" + "sbc r15,%B2 \n\t" + "brcc d32u_3%= \n\t" + "add r14,%A2 \n\t" + "adc r15,%B2 \n\t" + "clc \n\t" + "rjmp d32u_1%= \n\t" + "d32u_3%=:" "sec \n\t" + "rjmp d32u_1%= \n\t" + + "end%=:" // end + :"=&r"(a) + :"0"(a),"r"(b) + :"r14","r15","r16" + ); + return a; +#else + return a/b; +#endif +} + +const uint16_t fast_div_lut[17] PROGMEM = {0,F_CPU/4096,F_CPU/8192,F_CPU/12288,F_CPU/16384,F_CPU/20480,F_CPU/24576,F_CPU/28672,F_CPU/32768,F_CPU/36864 + ,F_CPU/40960,F_CPU/45056,F_CPU/49152,F_CPU/53248,F_CPU/57344,F_CPU/61440,F_CPU/65536}; + +const uint16_t slow_div_lut[257] PROGMEM = {0,F_CPU/32,F_CPU/64,F_CPU/96,F_CPU/128,F_CPU/160,F_CPU/192,F_CPU/224,F_CPU/256,F_CPU/288,F_CPU/320,F_CPU/352 + ,F_CPU/384,F_CPU/416,F_CPU/448,F_CPU/480,F_CPU/512,F_CPU/544,F_CPU/576,F_CPU/608,F_CPU/640,F_CPU/672,F_CPU/704,F_CPU/736,F_CPU/768,F_CPU/800,F_CPU/832 + ,F_CPU/864,F_CPU/896,F_CPU/928,F_CPU/960,F_CPU/992,F_CPU/1024,F_CPU/1056,F_CPU/1088,F_CPU/1120,F_CPU/1152,F_CPU/1184,F_CPU/1216,F_CPU/1248,F_CPU/1280,F_CPU/1312 + ,F_CPU/1344,F_CPU/1376,F_CPU/1408,F_CPU/1440,F_CPU/1472,F_CPU/1504,F_CPU/1536,F_CPU/1568,F_CPU/1600,F_CPU/1632,F_CPU/1664,F_CPU/1696,F_CPU/1728,F_CPU/1760,F_CPU/1792 + ,F_CPU/1824,F_CPU/1856,F_CPU/1888,F_CPU/1920,F_CPU/1952,F_CPU/1984,F_CPU/2016 + ,F_CPU/2048,F_CPU/2080,F_CPU/2112,F_CPU/2144,F_CPU/2176,F_CPU/2208,F_CPU/2240,F_CPU/2272,F_CPU/2304,F_CPU/2336,F_CPU/2368,F_CPU/2400 + ,F_CPU/2432,F_CPU/2464,F_CPU/2496,F_CPU/2528,F_CPU/2560,F_CPU/2592,F_CPU/2624,F_CPU/2656,F_CPU/2688,F_CPU/2720,F_CPU/2752,F_CPU/2784,F_CPU/2816,F_CPU/2848,F_CPU/2880 + ,F_CPU/2912,F_CPU/2944,F_CPU/2976,F_CPU/3008,F_CPU/3040,F_CPU/3072,F_CPU/3104,F_CPU/3136,F_CPU/3168,F_CPU/3200,F_CPU/3232,F_CPU/3264,F_CPU/3296,F_CPU/3328,F_CPU/3360 + ,F_CPU/3392,F_CPU/3424,F_CPU/3456,F_CPU/3488,F_CPU/3520,F_CPU/3552,F_CPU/3584,F_CPU/3616,F_CPU/3648,F_CPU/3680,F_CPU/3712,F_CPU/3744,F_CPU/3776,F_CPU/3808,F_CPU/3840 + ,F_CPU/3872,F_CPU/3904,F_CPU/3936,F_CPU/3968,F_CPU/4000,F_CPU/4032,F_CPU/4064 + ,F_CPU/4096,F_CPU/4128,F_CPU/4160,F_CPU/4192,F_CPU/4224,F_CPU/4256,F_CPU/4288,F_CPU/4320,F_CPU/4352,F_CPU/4384,F_CPU/4416,F_CPU/4448,F_CPU/4480,F_CPU/4512,F_CPU/4544 + ,F_CPU/4576,F_CPU/4608,F_CPU/4640,F_CPU/4672,F_CPU/4704,F_CPU/4736,F_CPU/4768,F_CPU/4800,F_CPU/4832,F_CPU/4864,F_CPU/4896,F_CPU/4928,F_CPU/4960,F_CPU/4992,F_CPU/5024 + ,F_CPU/5056,F_CPU/5088,F_CPU/5120,F_CPU/5152,F_CPU/5184,F_CPU/5216,F_CPU/5248,F_CPU/5280,F_CPU/5312,F_CPU/5344,F_CPU/5376,F_CPU/5408,F_CPU/5440,F_CPU/5472,F_CPU/5504 + ,F_CPU/5536,F_CPU/5568,F_CPU/5600,F_CPU/5632,F_CPU/5664,F_CPU/5696,F_CPU/5728,F_CPU/5760,F_CPU/5792,F_CPU/5824,F_CPU/5856,F_CPU/5888,F_CPU/5920,F_CPU/5952,F_CPU/5984 + ,F_CPU/6016,F_CPU/6048,F_CPU/6080,F_CPU/6112,F_CPU/6144,F_CPU/6176,F_CPU/6208,F_CPU/6240,F_CPU/6272,F_CPU/6304,F_CPU/6336,F_CPU/6368,F_CPU/6400,F_CPU/6432,F_CPU/6464 + ,F_CPU/6496,F_CPU/6528,F_CPU/6560,F_CPU/6592,F_CPU/6624,F_CPU/6656,F_CPU/6688,F_CPU/6720,F_CPU/6752,F_CPU/6784,F_CPU/6816,F_CPU/6848,F_CPU/6880,F_CPU/6912,F_CPU/6944 + ,F_CPU/6976,F_CPU/7008,F_CPU/7040,F_CPU/7072,F_CPU/7104,F_CPU/7136,F_CPU/7168,F_CPU/7200,F_CPU/7232,F_CPU/7264,F_CPU/7296,F_CPU/7328,F_CPU/7360,F_CPU/7392,F_CPU/7424 + ,F_CPU/7456,F_CPU/7488,F_CPU/7520,F_CPU/7552,F_CPU/7584,F_CPU/7616,F_CPU/7648,F_CPU/7680,F_CPU/7712,F_CPU/7744,F_CPU/7776,F_CPU/7808,F_CPU/7840,F_CPU/7872,F_CPU/7904 + ,F_CPU/7936,F_CPU/7968,F_CPU/8000,F_CPU/8032,F_CPU/8064,F_CPU/8096,F_CPU/8128,F_CPU/8160,F_CPU/8192 + }; +/** \brief approximates division of F_CPU/divisor + +In the stepper interrupt a division is needed, which is a slow operation. +The result is used for timer calculation where small errors are ok. This +function uses lookup tables to find a fast approximation of the result. + +*/ +long CPUDivU2(unsigned int divisor) { +#if CPU_ARCH==ARCH_AVR + long res; + unsigned short table; + if(divisor<8192) { + if(divisor<512) { + if(divisor<10) divisor = 10; + return Div4U2U(F_CPU,divisor); // These entries have overflows in lookuptable! + } + table = (unsigned short)&slow_div_lut[0]; + __asm__ __volatile__( // needs 64 ticks neu 49 Ticks + "mov r18,%A1 \n\t" + "andi r18,31 \n\t" // divisor & 31 in r18 + "lsr %B1 \n\t" // divisor >> 4 + "ror %A1 \n\t" + "lsr %B1 \n\t" + "ror %A1 \n\t" + "lsr %B1 \n\t" + "ror %A1 \n\t" + "lsr %B1 \n\t" + "ror %A1 \n\t" + "andi %A1,254 \n\t" + "add %A2,%A1 \n\t" // table+divisor>>3 + "adc %B2,%B1 \n\t" + "lpm %A0,Z+ \n\t" // y0 in res + "lpm %B0,Z+ \n\t" // %C0,%D0 are 0 + "movw r4,%A0 \n\t" // y0 nach gain (r4-r5) + "lpm r0,Z+ \n\t" // gain = gain-y1 + "sub r4,r0 \n\t" + "lpm r0,Z+ \n\t" + "sbc r5,r0 \n\t" + "mul r18,r4 \n\t" // gain*(divisor & 31) + "movw %A1,r0 \n\t" // divisor not needed any more, use for byte 0,1 of result + "mul r18,r5 \n\t" + "add %B1,r0 \n\t" + "mov %A2,r1 \n\t" + "lsl %A1 \n\t" + "rol %B1 \n\t" + "rol %A2 \n\t" + "lsl %A1 \n\t" + "rol %B1 \n\t" + "rol %A2 \n\t" + "lsl %A1 \n\t" + "rol %B1 \n\t" + "rol %A2 \n\t" + "sub %A0,%B1 \n\t" + "sbc %B0,%A2 \n\t" + "clr %C0 \n\t" + "clr %D0 \n\t" + "clr r1 \n\t" + : "=&r" (res),"=&d"(divisor),"=&z"(table) : "1"(divisor),"2"(table) : "r18","r4","r5"); + return res; + /*unsigned short adr0 = (unsigned short)&slow_div_lut+(divisor>>4)&1022; + long y0= pgm_read_dword_near(adr0); + long gain = y0-pgm_read_dword_near(adr0+2); + return y0-((gain*(divisor & 31))>>5);*/ + } else { + table = (unsigned short)&fast_div_lut[0]; + __asm__ __volatile__( // needs 49 ticks + "movw r18,%A1 \n\t" + "andi r19,15 \n\t" // divisor & 4095 in r18,r19 + "lsr %B1 \n\t" // divisor >> 3, then %B1 is 2*(divisor >> 12) + "lsr %B1 \n\t" + "lsr %B1 \n\t" + "andi %B1,254 \n\t" + "add %A2,%B1 \n\t" // table+divisor>>11 + "adc %B2,r1 \n\t" // + "lpm %A0,Z+ \n\t" // y0 in res + "lpm %B0,Z+ \n\t" + "movw r4,%A0 \n\t" // y0 to gain (r4-r5) + "lpm r0,Z+ \n\t" // gain = gain-y1 + "sub r4,r0 \n\t" + "lpm r0,Z+ \n\t" + "sbc r5,r0 \n\t" // finished - result has max. 16 bit + "mul r18,r4 \n\t" // gain*(divisor & 4095) + "movw %A1,r0 \n\t" // divisor not needed any more, use for byte 0,1 of result + "mul r19,r5 \n\t" + "mov %A2,r0 \n\t" // %A2 = byte 3 of result + "mul r18,r5 \n\t" + "add %B1,r0 \n\t" + "adc %A2,r1 \n\t" + "mul r19,r4 \n\t" + "add %B1,r0 \n\t" + "adc %A2,r1 \n\t" + "andi %B1,240 \n\t" // >> 12 + "swap %B1 \n\t" + "swap %A2 \r\n" + "mov %A1,%A2 \r\n" + "andi %A1,240 \r\n" + "or %B1,%A1 \r\n" + "andi %A2,15 \r\n" + "sub %A0,%B1 \n\t" + "sbc %B0,%A2 \n\t" + "clr %C0 \n\t" + "clr %D0 \n\t" + "clr r1 \n\t" + : "=&r" (res),"=&d"(divisor),"=&z"(table) : "1"(divisor),"2"(table) : "r18","r19","r4","r5"); + return res; + /* + // The asm mimics the following code + unsigned short adr0 = (unsigned short)&fast_div_lut+(divisor>>11)&254; + unsigned short y0= pgm_read_word_near(adr0); + unsigned short gain = y0-pgm_read_word_near(adr0+2); + return y0-(((long)gain*(divisor & 4095))>>12);*/ +#else + return F_CPU/divisor; +#endif + } +} + +/** + \brief Sets the destination coordinates to values stored in com. + + For the computation of the destination, the following facts are considered: + - Are units inches or mm. + - Reltive or absolute positioning with special case only extruder relative. + - Offset in x and y direction for multiple extruder support. +*/ +byte get_coordinates(GCode *com) +{ + register long p; + register byte r=0; + if(lines_count==0) { + UI_STATUS(UI_TEXT_PRINTING); + } + if(GCODE_HAS_X(com)) { + r = 1; + if(unit_inches) + p = com->X*25.4*axis_steps_per_unit[0]; + else + p = com->X*axis_steps_per_unit[0]; + if(relative_mode) + printer_state.destinationSteps[0] = printer_state.currentPositionSteps[0]+p; + else + printer_state.destinationSteps[0] = p+printer_state.offsetX; + } else printer_state.destinationSteps[0] = printer_state.currentPositionSteps[0]; + if(GCODE_HAS_Y(com)) { + r = 1; + if(unit_inches) + p = com->Y*25.4*axis_steps_per_unit[1]; + else + p = com->Y*axis_steps_per_unit[1]; + if(relative_mode) + printer_state.destinationSteps[1] = printer_state.currentPositionSteps[1]+p; + else + printer_state.destinationSteps[1] = p+printer_state.offsetY; + } else printer_state.destinationSteps[1] = printer_state.currentPositionSteps[1]; + if(GCODE_HAS_Z(com)) { + r = 1; + if(unit_inches) + p = com->Z*25.4*axis_steps_per_unit[2]; + else + p = com->Z*axis_steps_per_unit[2]; + if(relative_mode) { + printer_state.destinationSteps[2] = printer_state.currentPositionSteps[2]+p; + } else { + printer_state.destinationSteps[2] = p; + } + } else printer_state.destinationSteps[2] = printer_state.currentPositionSteps[2]; + if(GCODE_HAS_E(com) && !DEBUG_DRYRUN) { + if(unit_inches) + p = com->E*25.4*axis_steps_per_unit[3]; + else + p = com->E*axis_steps_per_unit[3]; + if(relative_mode || relative_mode_e) + printer_state.destinationSteps[3] = printer_state.currentPositionSteps[3]+p; + else + printer_state.destinationSteps[3] = p; + } else printer_state.destinationSteps[3] = printer_state.currentPositionSteps[3]; + if(GCODE_HAS_F(com)) { + if(com->F < 1) + printer_state.feedrate = 1; + else + if(unit_inches) + printer_state.feedrate = com->F*0.0042333f*(float)printer_state.feedrateMultiply; // Factor is 25.5/60/100 + else + printer_state.feedrate = com->F*(float)printer_state.feedrateMultiply*0.00016666666f; + } + return r || (GCODE_HAS_E(com) && printer_state.destinationSteps[3]!=printer_state.currentPositionSteps[3]); // ignore unproductive moves +} +inline unsigned int ComputeV(long timer,long accel) { +#if CPU_ARCH==ARCH_AVR + unsigned int res; + // 38 Ticks + __asm__ __volatile__ ( // 0 = res, 1 = timer, 2 = accel %D2=0 ,%A1 are unused is free + // Result LSB first: %A0, %B0, %A1 + "mul %B1,%A2 \n\t" + "mov %A0,r1 \n\t" + "mul %B1,%C2 \n\t" + "mov %B0,r0 \n\t" + "mov %A1,r1 \n\t" + "mul %B1,%B2 \n\t" + "add %A0,r0 \n\t" + "adc %B0,r1 \n\t" + "adc %A1,%D2 \n\t" + "mul %C1,%A2 \n\t" + "add %A0,r0 \n\t" + "adc %B0,r1 \n\t" + "adc %A1,%D2 \n\t" + "mul %C1,%B2 \n\t" + "add %B0,r0 \n\t" + "adc %A1,r1 \n\t" + "mul %D1,%A2 \n\t" + "add %B0,r0 \n\t" + "adc %A1,r1 \n\t" + "mul %C1,%C2 \n\t" + "add %A1,r0 \n\t" + "mul %D1,%B2 \n\t" + "add %A1,r0 \n\t" + "lsr %A1 \n\t" + "ror %B0 \n\t" + "ror %A0 \n\t" + "lsr %A1 \n\t" + "ror %B0 \n\t" + "ror %A0 \n\t" + "clr r1 \n\t" + :"=&r"(res),"=r"(timer),"=r"(accel) + :"1"(timer),"2"(accel) + : ); + // unsigned int v = ((timer>>8)*cur->accel)>>10; + return res; +#else + return ((timer>>8)*accel)>>10; +#endif +} +// Multiply two 16 bit values and return 32 bit result +inline unsigned long mulu6xu16to32(unsigned int a,unsigned int b) { + unsigned long res; + // 18 Ticks = 1.125 us + __asm__ __volatile__ ( // 0 = res, 1 = timer, 2 = accel %D2=0 ,%A1 are unused is free + // Result LSB first: %A0, %B0, %A1 + "clr r18 \n\t" + "mul %B2,%B1 \n\t" // mul hig bytes + "movw %C0,r0 \n\t" + "mul %A1,%A2 \n\t" // mul low bytes + "movw %A0,r0 \n\t" + "mul %A1,%B2 \n\t" + "add %B0,r0 \n\t" + "adc %C0,r1 \n\t" + "adc %D0,r18 \n\t" + "mul %B1,%A2 \n\t" + "add %B0,r0 \n\t" + "adc %C0,r1 \n\t" + "adc %D0,r18 \n\t" + "clr r1 \n\t" + :"=&r"(res),"=r"(a),"=r"(b) + :"1"(a),"2"(b) + :"r18" ); + // return (long)a*b; + return res; +} +// Multiply two 16 bit values and return 32 bit result +inline unsigned int mulu6xu16shift16(unsigned int a,unsigned int b) { +#if CPU_ARCH==ARCH_AVR + unsigned int res; + // 18 Ticks = 1.125 us + __asm__ __volatile__ ( // 0 = res, 1 = timer, 2 = accel %D2=0 ,%A1 are unused is free + // Result LSB first: %A0, %B0, %A1 + "clr r18 \n\t" + "mul %B2,%B1 \n\t" // mul hig bytes + "movw %A0,r0 \n\t" + "mul %A1,%A2 \n\t" // mul low bytes + "mov r19,r1 \n\t" + "mul %A1,%B2 \n\t" + "add r19,r0 \n\t" + "adc %A0,r1 \n\t" + "adc %B0,r18 \n\t" + "mul %B1,%A2 \n\t" + "add r19,r0 \n\t" + "adc %A0,r1 \n\t" + "adc %B0,r18 \n\t" + "clr r1 \n\t" + :"=&r"(res),"=r"(a),"=r"(b) + :"1"(a),"2"(b) + :"r18","r19" ); + return res; +#else + return ((long)a*b)>>16; +#endif +} + +/** + Moves the stepper motors one step. If the last step is reached, the next movement is started. + The function must be called from a timer loop. It returns the time for the next call. + This is a modified version that implements a bresenham 'multi-step' algorithm where the dominant + cartesian axis steps may be less than the changing dominant delta axis. +*/ +#if DRIVE_SYSTEM==3 +int lastblk=-1; +long cur_errupd; +//#define DEBUG_DELTA_TIMER +// Current delta segment +DeltaSegment *curd; +// Current delta segment primary error increment +long curd_errupd, stepsPerSegRemaining; +inline long bresenham_step() { + if(cur == 0) { + sei(); + cur = &lines[lines_pos]; + if(cur->flags & FLAG_BLOCKED) { // This step is in computation - shouldn't happen + if(lastblk!=(int)cur) { + lastblk = (int)cur; + out.println_int_P(PSTR("BLK "),(unsigned int)lines_count); + } + cur = 0; + return 2000; + } + lastblk = -1; + #ifdef INCLUDE_DEBUG_NO_MOVE + if(DEBUG_NO_MOVES) { // simulate a move, but do nothing in reality + lines_pos++; + if(lines_pos>=MOVE_CACHE_SIZE) lines_pos=0; + cur = 0; + cli(); + --lines_count; + return 1000; + } + #endif + if(cur->flags & FLAG_WARMUP) { + // This is a warmup move to initalize the path planner correctly. Just waste + // a bit of time to get the planning up to date. + #if USE_OPS==1 + if(cur->joinFlags & FLAG_JOIN_END_RETRACT) { // Make sure filament is pushed back + if(printer_state.filamentRetracted) { + #ifdef DEBUG_OPS + //sei(); + out.println_P(PSTR("DownW")); + cli(); + #endif + printer_state.filamentRetracted = false; + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + } + if(printer_state.extruderStepsNeeded) { + cur = 0; + return 2000; // wait, work is done in other interrupt + } + } else if(cur->joinFlags & FLAG_JOIN_START_RETRACT) { + if(!printer_state.filamentRetracted) { + #ifdef DEBUG_OPS + //sei(); + out.println_P(PSTR("UpW")); + cli(); + #endif + printer_state.filamentRetracted = true; + cli(); + printer_state.extruderStepsNeeded-=printer_state.opsRetractSteps; + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_UP; + } + if(printer_state.extruderStepsNeeded) { + cur = 0; + return 2000; // wait, work is done in other interrupt + } + } + #endif + if(lines_count<=cur->primaryAxis) { cur=0;return 2000;} + lines_pos++; + if(lines_pos>=MOVE_CACHE_SIZE) lines_pos=0; + long wait = cur->accelerationPrim; + cur = 0; + --lines_count; + return(wait); // waste some time for path optimization to fill up + } // End if WARMUP + if(cur->dir & 128) extruder_enable(); + cur->joinFlags |= FLAG_JOIN_END_FIXED | FLAG_JOIN_START_FIXED; // don't touch this segment any more, just for safety + #if USE_OPS==1 + if(printer_state.opsMode) { // Enabled? + if(cur->joinFlags & FLAG_JOIN_START_RETRACT) { + if(!printer_state.filamentRetracted) { + #ifdef DEBUG_OPS + out.println_P(PSTR("Up")); + #endif + printer_state.filamentRetracted = true; + cli(); + printer_state.extruderStepsNeeded-=printer_state.opsRetractSteps; + sei(); + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_UP; + } + } + // If path optimizer ran out of samples, he might miss some retractions. Solve them before printing + else if((cur->joinFlags & FLAG_JOIN_NO_RETRACT)==0 && printmoveSeen) { + if((cur->dir & 136)==136) { + if(printer_state.filamentRetracted) { // Printmove and filament is still up! + #ifdef DEBUG_OPS + out.println_P(PSTR("DownA")); + #endif + printer_state.filamentRetracted = false; + cli(); + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_DOWN; + } + } /*else if(!printer_state.filamentRetracted) { +#ifdef DEBUG_OPS + out.println_P(PSTR("UpA")); +#endif + printer_state.filamentRetracted = true; + cli(); + printer_state.extruderStepsNeeded-=printer_state.opsRetractSteps; + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_UP; + }*/ + } + if(cur->joinFlags & FLAG_JOIN_WAIT_EXTRUDER_UP) { // Wait for filament pushback + cli(); + if(printer_state.extruderStepsNeededjoinFlags & FLAG_JOIN_WAIT_EXTRUDER_DOWN) { // Wait for filament pushback + cli(); + if(printer_state.extruderStepsNeeded) { + cur=0; + return 4000; + } + } + } // End if opsMode + #endif + sei(); // Allow interrupts + // Set up delta segments + if (cur->numDeltaSegments) { + // If there are delta segments point to them here + curd = &segments[cur->deltaSegmentReadPos++]; + if (cur->deltaSegmentReadPos >= DELTA_CACHE_SIZE) cur->deltaSegmentReadPos=0; + // Enable axis - All axis are enabled since they will most probably all be involved in a move + // Since segments could involve different axis this reduces load when switching segments and + // makes disabling easier. + enable_x();enable_y();enable_z(); + + // Copy across movement into main direction flags so that endstops function correctly + cur->dir |= curd->dir; + // Initialize bresenham for the first segment + if (cur->halfstep) { + cur->error[0] = cur->error[1] = cur->error[2] = cur->numPrimaryStepPerSegment; + curd_errupd = cur->numPrimaryStepPerSegment = cur->numPrimaryStepPerSegment<<1; + } else { + cur->error[0] = cur->error[1] = cur->error[2] = cur->numPrimaryStepPerSegment>>1; + curd_errupd = cur->numPrimaryStepPerSegment; + } + stepsPerSegRemaining = cur->numPrimaryStepPerSegment; + #ifdef DEBUG_DELTA_TIMER + out.println_byte_P(PSTR("HS: "),cur->halfstep); + out.println_long_P(PSTR("Error: "),curd_errupd); + #endif + } else curd=0; + cur_errupd = (cur->halfstep ? cur->stepsRemaining << 1 : cur->stepsRemaining); + + if(!(cur->joinFlags & FLAG_JOIN_STEPPARAMS_COMPUTED)) {// should never happen, but with bad timings??? + out.println_int_P(PSTR("LATE "),(unsigned int)lines_count); + updateStepsParameter(cur/*,8*/); + } + printer_state.vMaxReached = cur->vStart; + printer_state.stepNumber=0; + printer_state.timer = 0; + cli(); + //Determine direction of movement + if (curd) { + if(curd->dir & 1) { + WRITE(X_DIR_PIN,!INVERT_X_DIR); + } else { + WRITE(X_DIR_PIN,INVERT_X_DIR); + } + if(curd->dir & 2) { + WRITE(Y_DIR_PIN,!INVERT_Y_DIR); + } else { + WRITE(Y_DIR_PIN,INVERT_Y_DIR); + } + if(curd->dir & 4) { + WRITE(Z_DIR_PIN,!INVERT_Z_DIR); + } else { + WRITE(Z_DIR_PIN,INVERT_Z_DIR); + } + } + #if USE_OPS==1 || defined(USE_ADVANCE) + if((printer_state.flag0 & PRINTER_FLAG0_SEPERATE_EXTRUDER_INT)==0) // Set direction if no advance/OPS enabled + #endif + if(cur->dir & 8) { + extruder_set_direction(1); + } else { + extruder_set_direction(0); + } + #ifdef USE_ADVANCE + long h = mulu6xu16to32(cur->vStart,cur->advanceL); + int tred = (( + #ifdef ENABLE_QUADRATIC_ADVANCE + (printer_state.advance_executed = cur->advanceStart)+ + #endif + h)>>16); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + #endif + if(printer_state.waslasthalfstepping && cur->halfstep==0) { // Switch halfstepping -> full stepping + printer_state.waslasthalfstepping = 0; + return printer_state.interval*3; // Wait an other 150% from last half step to make the 100% full + } else if(!printer_state.waslasthalfstepping && cur->halfstep) { // Switch full to half stepping + printer_state.waslasthalfstepping = 1; + } else + return printer_state.interval; // Wait an other 50% from last step to make the 100% full + } // End cur=0 + sei(); + + /* For halfstepping, we divide the actions into even and odd actions to split + time used per loop. */ + byte do_even; + byte do_odd; + if(cur->halfstep) { + do_odd = cur->halfstep & 1; + do_even = cur->halfstep & 2; + cur->halfstep = 3-cur->halfstep; + } else { + do_even = 1; + do_odd = 1; + } + cli(); + if(do_even) { + if((cur->flags & FLAG_CHECK_ENDSTOPS) && (curd != 0)) { + #if X_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_X + if((curd->dir & 17)==17) if(READ(X_MAX_PIN) != ENDSTOP_X_MAX_INVERTING) { + curd->dir&=~16; + cur->dir&=~16; + } + #endif + #if Y_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_Y + if((curd->dir & 34)==34) if(READ(Y_MAX_PIN) != ENDSTOP_Y_MAX_INVERTING) { + curd->dir&=~32; + cur->dir&=~32; + } + #endif + #if Z_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_Z + if((curd->dir & 68)==68) if(READ(Z_MAX_PIN)!= ENDSTOP_Z_MAX_INVERTING) { + curd->dir&=~64; + cur->dir&=~64; + } + #endif + } + } + byte max_loops = (printer_state.stepper_loops<=cur->stepsRemaining ? printer_state.stepper_loops : cur->stepsRemaining); + if(cur->stepsRemaining>0) { + for(byte loop=0;loop0) + #if STEPPER_HIGH_DELAY>0 + delayMicroseconds(STEPPER_HIGH_DELAY+DOUBLE_STEP_DELAY); + #else + delayMicroseconds(DOUBLE_STEP_DELAY); + #endif + if(cur->dir & 128) { + if((cur->error[3] -= cur->delta[3]) < 0) { + #if USE_OPS==1 || defined(USE_ADVANCE) + if((printer_state.flag0 & PRINTER_FLAG0_SEPERATE_EXTRUDER_INT)) { // Use interrupt for movement + if(cur->dir & 8) + printer_state.extruderStepsNeeded++; + else + printer_state.extruderStepsNeeded--; + } else { + #endif + extruder_step(); + #if USE_OPS==1 || defined(USE_ADVANCE) + } + #endif + cur->error[3] += cur_errupd; + } + } + if (curd) { + // Take delta steps + if(curd->dir & 16) { + if((cur->error[0] -= curd->deltaSteps[0]) < 0) { + WRITE(X_STEP_PIN,HIGH); + cur->error[0] += curd_errupd; + #ifdef DEBUG_STEPCOUNT + cur->totalStepsRemaining--; + #endif + } + } + + if(curd->dir & 32) { + if((cur->error[1] -= curd->deltaSteps[1]) < 0) { + WRITE(Y_STEP_PIN,HIGH); + cur->error[1] += curd_errupd; + #ifdef DEBUG_STEPCOUNT + cur->totalStepsRemaining--; + #endif + } + } + + if(curd->dir & 64) { + if((cur->error[2] -= curd->deltaSteps[2]) < 0) { + WRITE(Z_STEP_PIN,HIGH); + printer_state.countZSteps += ( cur->dir & 4 ? 1 : -1 ); + cur->error[2] += curd_errupd; + #ifdef DEBUG_STEPCOUNT + cur->totalStepsRemaining--; + #endif + } + } + + #if STEPPER_HIGH_DELAY>0 + delayMicroseconds(STEPPER_HIGH_DELAY); + #endif + WRITE(X_STEP_PIN,LOW); + WRITE(Y_STEP_PIN,LOW); + WRITE(Z_STEP_PIN,LOW); + stepsPerSegRemaining--; + if (!stepsPerSegRemaining) { + cur->numDeltaSegments--; + if (cur->numDeltaSegments) { + + // Get the next delta segment + curd = &segments[cur->deltaSegmentReadPos++]; + if (cur->deltaSegmentReadPos >= DELTA_CACHE_SIZE) cur->deltaSegmentReadPos=0; + delta_segment_count--; + + // Initialize bresenham for this segment (numPrimaryStepPerSegment is already correct for the half step setting) + cur->error[0] = cur->error[1] = cur->error[2] = cur->numPrimaryStepPerSegment>>1; + + // Reset the counter of the primary steps. This is initialized in the line + // generation so don't have to do this the first time. + stepsPerSegRemaining = cur->numPrimaryStepPerSegment; + + // Change direction if necessary + if(curd->dir & 1) { + WRITE(X_DIR_PIN,!INVERT_X_DIR); + } else { + WRITE(X_DIR_PIN,INVERT_X_DIR); + } + if(curd->dir & 2) { + WRITE(Y_DIR_PIN,!INVERT_Y_DIR); + } else { + WRITE(Y_DIR_PIN,INVERT_Y_DIR); + } + if(curd->dir & 4) { + WRITE(Z_DIR_PIN,!INVERT_Z_DIR); + } else { + WRITE(Z_DIR_PIN,INVERT_Z_DIR); + } + } else { + // Release the last segment + delta_segment_count--; + curd=0; + } + } + } + #if USE_OPS==1 || defined(USE_ADVANCE) + if((printer_state.flag0 & PRINTER_FLAG0_SEPERATE_EXTRUDER_INT)==0) // Use interrupt for movement + #endif + extruder_unstep(); + } // for loop + if(do_odd) { + sei(); // Allow interrupts for other types, timer1 is still disabled + #ifdef RAMP_ACCELERATION + //If acceleration is enabled on this move and we are in the acceleration segment, calculate the current interval + if (printer_state.stepNumber <= cur->accelSteps) { // we are accelerating + printer_state.vMaxReached = ComputeV(printer_state.timer,cur->facceleration)+cur->vStart; + if(printer_state.vMaxReached>cur->vMax) printer_state.vMaxReached = cur->vMax; + unsigned int v; + if(printer_state.vMaxReached>STEP_DOUBLER_FREQUENCY) { + #if ALLOW_QUADSTEPPING + if(printer_state.vMaxReached>STEP_DOUBLER_FREQUENCY*2) { + printer_state.stepper_loops = 4; + v = printer_state.vMaxReached>>2; + } else { + printer_state.stepper_loops = 2; + v = printer_state.vMaxReached>>1; + } + #else + printer_state.stepper_loops = 2; + v = printer_state.vMaxReached>>1; + #endif + } else { + printer_state.stepper_loops = 1; + v = printer_state.vMaxReached; + } + printer_state.interval = CPUDivU2(v); + printer_state.timer+=printer_state.interval; + #ifdef USE_ADVANCE + #ifdef ENABLE_QUADRATIC_ADVANCE + long advance_target =printer_state.advance_executed+cur->advanceRate; + for(byte loop=1;loopadvanceRate; + if(advance_target>cur->advanceFull) + advance_target = cur->advanceFull; + cli(); + long h = mulu6xu16to32(printer_state.vMaxReached,cur->advanceL); + int tred = ((advance_target+h)>>16); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + printer_state.advance_executed = advance_target; + #else + int tred = mulu6xu16shift16(printer_state.vMaxReached,cur->advanceL); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + #endif + #endif + } else if (cur->stepsRemaining <= cur->decelSteps) { // time to slow down + if (!(cur->flags & FLAG_DECELERATING)) { + printer_state.timer = 0; + cur->flags |= FLAG_DECELERATING; + } + unsigned int v = ComputeV(printer_state.timer,cur->facceleration); + if (v > printer_state.vMaxReached) // if deceleration goes too far it can become too large + v = cur->vEnd; + else { + v=printer_state.vMaxReached-v; + if (vvEnd) v = cur->vEnd; // extra steps at the end of desceleration due to rounding erros + } + if(v>STEP_DOUBLER_FREQUENCY) { + #if ALLOW_QUADSTEPPING + if(v>STEP_DOUBLER_FREQUENCY*2) { + printer_state.stepper_loops = 4; + v = v>>2; + } else { + printer_state.stepper_loops = 2; + v = v>>1; + } + #else + printer_state.stepper_loops = 2; + v = v>>1; + #endif + } else { + printer_state.stepper_loops = 1; + } + printer_state.interval = CPUDivU2(v); + printer_state.timer+=printer_state.interval; + #ifdef USE_ADVANCE + #ifdef ENABLE_QUADRATIC_ADVANCE + long advance_target =printer_state.advance_executed-cur->advanceRate; + for(byte loop=1;loopadvanceRate; + if(advance_targetadvanceEnd) + advance_target = cur->advanceEnd; + long h=mulu6xu16to32(cur->advanceL,v); + int tred = ((advance_target+h)>>16); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + printer_state.advance_executed = advance_target; + #else + int tred=mulu6xu16shift16(cur->advanceL,v); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + #endif + #endif + } else { + // If we had acceleration, we need to use the latest vMaxReached and interval + // If we started full speed, we need to use cur->fullInterval and vMax + #ifdef USE_ADVANCE + unsigned int v; + if(!cur->accelSteps) { + v = cur->vMax; + } else { + v = printer_state.vMaxReached; + } + #ifdef ENABLE_QUADRATIC_ADVANCE + long h=mulu6xu16to32(cur->advanceL,v); + int tred = ((printer_state.advance_executed+h)>>16); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + #else + int tred=mulu6xu16shift16(cur->advanceL,v); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + #endif + #endif + if(!cur->accelSteps) { + if(cur->vMax>STEP_DOUBLER_FREQUENCY) { + #if ALLOW_QUADSTEPPING + if(cur->vMax>STEP_DOUBLER_FREQUENCY*2) { + printer_state.stepper_loops = 4; + printer_state.interval = cur->fullInterval>>2; + } else { + printer_state.stepper_loops = 2; + printer_state.interval = cur->fullInterval>>1; + } + #else + printer_state.stepper_loops = 2; + printer_state.interval = cur->fullInterval>>1; + #endif + } else { + printer_state.stepper_loops = 1; + printer_state.interval = cur->fullInterval; + } + } + } + #else + printer_state.interval = cur->fullInterval; // without RAMPS always use full speed + #endif + } // do_odd + if(do_even) { + printer_state.stepNumber+=max_loops; + cur->stepsRemaining-=max_loops; + } + + #if USE_OPS==1 + if(printer_state.opsMode==2 && (cur->joinFlags & FLAG_JOIN_END_RETRACT) && printer_state.filamentRetracted && cur->stepsRemaining<=cur->opsReverseSteps) { + #ifdef DEBUG_OPS + out.println_long_P(PSTR("DownX"),cur->stepsRemaining); + #endif + // Point for retraction reversal reached. + printer_state.filamentRetracted = false; + cli(); + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + #ifdef DEBUG_OPS + sei(); + out.println_long_P(PSTR("N="),printer_state.extruderStepsNeeded); + #endif + } + #endif + } // stepsRemaining + long interval; + if(cur->halfstep) + interval = (printer_state.interval>>1); // time to come back + else + interval = printer_state.interval; + if(do_even) { + if(cur->stepsRemaining<=0 || (cur->dir & 240)==0) { // line finished +// out.println_int_P(PSTR("Line finished: "), (int) cur->numDeltaSegments); +// out.println_int_P(PSTR("DSC: "), (int) delta_segment_count); +// out.println_P(PSTR("F")); + + // Release remaining delta segments + delta_segment_count -= cur->numDeltaSegments; + #ifdef DEBUG_STEPCOUNT + if(cur->totalStepsRemaining) { + out.println_long_P(PSTR("Missed steps:"), cur->totalStepsRemaining); + out.println_long_P(PSTR("Step/seg r:"), stepsPerSegRemaining); + out.println_int_P(PSTR("NDS:"), (int) cur->numDeltaSegments); + out.println_int_P(PSTR("HS:"), (int) cur->halfstep); + } + #endif + + #if USE_OPS==1 + if(cur->joinFlags & FLAG_JOIN_END_RETRACT) { // Make sure filament is pushed back + sei(); + if(printer_state.filamentRetracted) { + #ifdef DEBUG_OPS + out.println_P(PSTR("Down")); + #endif + printer_state.filamentRetracted = false; + cli(); + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + } + cli(); + if(printer_state.extruderStepsNeeded) { + #ifdef DEBUG_OPS + // sei(); + // out.println_int_P(PSTR("W"),printer_state.extruderStepsNeeded); + #endif + return 4000; // wait, work is done in other interrupt + } + #ifdef DEBUG_OPS + sei(); + #endif + } + #endif + cli(); + lines_pos++; + if(lines_pos>=MOVE_CACHE_SIZE) lines_pos=0; + cur = 0; + --lines_count; + if(DISABLE_X) disable_x(); + if(DISABLE_Y) disable_y(); + if(DISABLE_Z) disable_z(); + if(lines_count==0) UI_STATUS(UI_TEXT_IDLE); + interval = printer_state.interval = interval>>1; // 50% of time to next call to do cur=0 + } + DEBUG_MEMORY; + } // Do even + return interval; +} +#else +/** + Moves the stepper motors one step. If the last step is reached, the next movement is started. + The function must be called from a timer loop. It returns the time for the next call. + + Normal non delta algorithm +*/ +int lastblk=-1; +long cur_errupd; +inline long bresenham_step() { + if(cur == 0) { + sei(); + ANALYZER_ON(ANALYZER_CH0); + cur = &lines[lines_pos]; + if(cur->flags & FLAG_BLOCKED) { // This step is in computation - shouldn't happen + if(lastblk!=(int)cur) { + lastblk = (int)cur; + out.println_int_P(PSTR("BLK "),(unsigned int)lines_count); + } + cur = 0; + return 2000; + } + lastblk = -1; +#ifdef INCLUDE_DEBUG_NO_MOVE + if(DEBUG_NO_MOVES) { // simulate a move, but do nothing in reality + NEXT_PLANNER_INDEX(lines_pos); + cur = 0; + cli(); + --lines_count; + return 1000; + } +#endif + ANALYZER_OFF(ANALYZER_CH0); + if(cur->flags & FLAG_WARMUP) { + // This is a warmup move to initalize the path planner correctly. Just waste + // a bit of time to get the planning up to date. +#if USE_OPS==1 + if(cur->joinFlags & FLAG_JOIN_END_RETRACT) { // Make sure filament is pushed back + if(printer_state.filamentRetracted) { + #ifdef DEBUG_OPS + //sei(); + OUT_P_LN("DownW"); + cli(); + #endif + printer_state.filamentRetracted = false; + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + } + if(printer_state.extruderStepsNeeded) { + cur = 0; + return 2000; // wait, work is done in other interrupt + } + } else if(cur->joinFlags & FLAG_JOIN_START_RETRACT) { + if(!printer_state.filamentRetracted) { + #ifdef DEBUG_OPS + OUT_P_LN("UpW"); + cli(); + #endif + printer_state.filamentRetracted = true; + cli(); + printer_state.extruderStepsNeeded-=printer_state.opsRetractSteps; + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_UP; + } + if(printer_state.extruderStepsNeeded) { + cur = 0; + return 2000; // wait, work is done in other interrupt + } + } +#endif + if(lines_count<=cur->primaryAxis) { + cur=0; + return 2000; + } + NEXT_PLANNER_INDEX(lines_pos); + long wait = cur->accelerationPrim; + cur = 0; + cli(); + --lines_count; + return(wait); // waste some time for path optimization to fill up + } // End if WARMUP +/*if(DEBUG_ECHO) { +OUT_P_L_LN("MSteps:",cur->stepsRemaining); + //OUT_P_F("Ln:",cur->startSpeed); +//OUT_P_F_LN(":",cur->endSpeed); +}*/ + //Only enable axis that are moving. If the axis doesn't need to move then it can stay disabled depending on configuration. +#ifdef XY_GANTRY + if(cur->dir & 48) { + enable_x(); + enable_y(); + } +#else + if(cur->dir & 16) enable_x(); + if(cur->dir & 32) enable_y(); +#endif + if(cur->dir & 64) { + enable_z(); + } + if(cur->dir & 128) extruder_enable(); + cur->joinFlags |= FLAG_JOIN_END_FIXED | FLAG_JOIN_START_FIXED; // don't touch this segment any more, just for safety +#if USE_OPS==1 + if(printer_state.opsMode) { // Enabled? + if(cur->joinFlags & FLAG_JOIN_START_RETRACT) { + if(!printer_state.filamentRetracted) { + #ifdef DEBUG_OPS + OUT_P_LN("Up"); + #endif + printer_state.filamentRetracted = true; + cli(); + printer_state.extruderStepsNeeded-=printer_state.opsRetractSteps; + sei(); + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_UP; + } + } + // If path optimizer ran out of samples, he might miss some retractions. Solve them before printing + else if((cur->joinFlags & FLAG_JOIN_NO_RETRACT)==0 && printmoveSeen) { + if((cur->dir & 136)==136) { + if(printer_state.filamentRetracted) { // Printmove and filament is still up! + #ifdef DEBUG_OPS + OUT_P_LN("DownA"); + #endif + printer_state.filamentRetracted = false; + cli(); + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_DOWN; + } + } /*else if(!printer_state.filamentRetracted) { +#ifdef DEBUG_OPS + out.println_P(PSTR("UpA")); +#endif + printer_state.filamentRetracted = true; + cli(); + printer_state.extruderStepsNeeded-=printer_state.opsRetractSteps; + cur->joinFlags |= FLAG_JOIN_WAIT_EXTRUDER_UP; + }*/ + } + if(cur->joinFlags & FLAG_JOIN_WAIT_EXTRUDER_UP) { // Wait for filament pushback + cli(); + if(printer_state.extruderStepsNeededjoinFlags & FLAG_JOIN_WAIT_EXTRUDER_DOWN) { // Wait for filament pushback + cli(); + if(printer_state.extruderStepsNeeded) { + cur=0; + return 4000; + } + } + } // End if opsMode +#endif + sei(); // Allow interrupts + if(cur->halfstep) { + cur_errupd = cur->delta[cur->primaryAxis]<<1; + //printer_state.interval = CPUDivU2(cur->vStart); + } else + cur_errupd = cur->delta[cur->primaryAxis]; + if(!(cur->joinFlags & FLAG_JOIN_STEPPARAMS_COMPUTED)) {// should never happen, but with bad timings??? + updateStepsParameter(cur/*,8*/); + } + printer_state.vMaxReached = cur->vStart; + printer_state.stepNumber=0; + printer_state.timer = 0; + cli(); + //Determine direction of movement,check if endstop was hit +#if !defined(XY_GANTRY) + if(cur->dir & 1) { + WRITE(X_DIR_PIN,!INVERT_X_DIR); + } else { + WRITE(X_DIR_PIN,INVERT_X_DIR); + } + if(cur->dir & 2) { + WRITE(Y_DIR_PIN,!INVERT_Y_DIR); + } else { + WRITE(Y_DIR_PIN,INVERT_Y_DIR); + } +#else + long gdx = (cur->dir & 1 ? cur->delta[0] : -cur->delta[0]); // Compute signed difference in steps + long gdy = (cur->dir & 2 ? cur->delta[1] : -cur->delta[1]); +#if DRIVE_SYSTEM==1 + if(gdx+gdy>=0) { + WRITE(X_DIR_PIN,!INVERT_X_DIR); + ANALYZER_ON(ANALYZER_CH4); + } else { + WRITE(X_DIR_PIN,INVERT_X_DIR); + ANALYZER_OFF(ANALYZER_CH4); + } + if(gdx>gdy) { + WRITE(Y_DIR_PIN,!INVERT_Y_DIR); + ANALYZER_ON(ANALYZER_CH5); + } else { + WRITE(Y_DIR_PIN,INVERT_Y_DIR); + ANALYZER_OFF(ANALYZER_CH5); + } +#endif +#if DRIVE_SYSTEM==2 + if(gdx+gdy>=0) { + WRITE(X_DIR_PIN,!INVERT_X_DIR); + } else { + WRITE(X_DIR_PIN,INVERT_X_DIR); + } + if(gdx<=gdy) { + WRITE(Y_DIR_PIN,!INVERT_Y_DIR); + } else { + WRITE(Y_DIR_PIN,INVERT_Y_DIR); + } +#endif +#endif + if(cur->dir & 4) { + WRITE(Z_DIR_PIN,!INVERT_Z_DIR); + } else { + WRITE(Z_DIR_PIN,INVERT_Z_DIR); + } +#if USE_OPS==1 || defined(USE_ADVANCE) + if((printer_state.flag0 & PRINTER_FLAG0_SEPERATE_EXTRUDER_INT)==0) // Set direction if no advance/OPS enabled +#endif + if(cur->dir & 8) { + extruder_set_direction(1); + } else { + extruder_set_direction(0); + } +#ifdef USE_ADVANCE + long h = mulu6xu16to32(cur->vStart,cur->advanceL); + int tred = (( +#ifdef ENABLE_QUADRATIC_ADVANCE + (printer_state.advance_executed = cur->advanceStart)+ +#endif + h)>>16); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; +#endif + if(printer_state.waslasthalfstepping && cur->halfstep==0) { // Switch halfstepping -> full stepping + printer_state.waslasthalfstepping = 0; + return printer_state.interval*3; // Wait an other 150% from last half step to make the 100% full + } else if(!printer_state.waslasthalfstepping && cur->halfstep) { // Switch full to half stepping + printer_state.waslasthalfstepping = 1; + } else + return printer_state.interval; // Wait an other 50% from last step to make the 100% full + } // End cur=0 + sei(); + /* For halfstepping, we divide the actions into even and odd actions to split + time used per loop. */ + byte do_even; + byte do_odd; + if(cur->halfstep) { + do_odd = cur->halfstep & 1; + do_even = cur->halfstep & 2; + cur->halfstep = 3-cur->halfstep; + } else { + do_even = 1; + do_odd = 1; + } + cli(); + if(do_even) { + if(cur->flags & FLAG_CHECK_ENDSTOPS) { +#if X_MIN_PIN>-1 && MIN_HARDWARE_ENDSTOP_X + if((cur->dir & 17)==16) if(READ(X_MIN_PIN) != ENDSTOP_X_MIN_INVERTING) { +#if DRIVE_SYSTEM==0 + cur->dir&=~16; +#else + cur->dir&=~48; +#endif + } +#endif +#if Y_MIN_PIN>-1 && MIN_HARDWARE_ENDSTOP_Y + if((cur->dir & 34)==32) if(READ(Y_MIN_PIN) != ENDSTOP_Y_MIN_INVERTING) { +#if DRIVE_SYSTEM==0 + cur->dir&=~32; +#else + cur->dir&=~48; +#endif + } +#endif +#if X_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_X + if((cur->dir & 17)==17) if(READ(X_MAX_PIN) != ENDSTOP_X_MAX_INVERTING) { +#if DRIVE_SYSTEM==0 + cur->dir&=~16; +#else + cur->dir&=~48; +#endif + } +#endif +#if Y_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_Y + if((cur->dir & 34)==34) if(READ(Y_MAX_PIN) != ENDSTOP_Y_MAX_INVERTING) { +#if DRIVE_SYSTEM==0 + cur->dir&=~32; +#else + cur->dir&=~48; +#endif + } +#endif + } + // Test Z-Axis every step if necessary, otherwise it could easyly ruin your printer! +#if Z_MIN_PIN>-1 && MIN_HARDWARE_ENDSTOP_Z + if((cur->dir & 68)==64) if(READ(Z_MIN_PIN) != ENDSTOP_Z_MIN_INVERTING) {cur->dir&=~64;} +#endif +#if Z_MAX_PIN>-1 && MAX_HARDWARE_ENDSTOP_Z + if((cur->dir & 68)==68) if(READ(Z_MAX_PIN)!= ENDSTOP_Z_MAX_INVERTING) {cur->dir&=~64;} +#endif + } + byte max_loops = (printer_state.stepper_loops<=cur->stepsRemaining ? printer_state.stepper_loops : cur->stepsRemaining); + if(cur->stepsRemaining>0) { + for(byte loop=0;loop0) +#if STEPPER_HIGH_DELAY>0 + delayMicroseconds(STEPPER_HIGH_DELAY+DOUBLE_STEP_DELAY); +#else + delayMicroseconds(DOUBLE_STEP_DELAY); +#endif + if(cur->dir & 128) { + if((cur->error[3] -= cur->delta[3]) < 0) { +#if USE_OPS==1 || defined(USE_ADVANCE) + if((printer_state.flag0 & PRINTER_FLAG0_SEPERATE_EXTRUDER_INT)) { // Use interrupt for movement + if(cur->dir & 8) + printer_state.extruderStepsNeeded++; + else + printer_state.extruderStepsNeeded--; + } else { +#endif + extruder_step(); +#if USE_OPS==1 || defined(USE_ADVANCE) + } +#endif + cur->error[3] += cur_errupd; + } + } +#if defined(XY_GANTRY) +#endif + if(cur->dir & 16) { + if((cur->error[0] -= cur->delta[0]) < 0) { + ANALYZER_ON(ANALYZER_CH6); +#if DRIVE_SYSTEM==0 || !defined(XY_GANTRY) + ANALYZER_ON(ANALYZER_CH2); + WRITE(X_STEP_PIN,HIGH); +#else +#if DRIVE_SYSTEM==1 + if(cur->dir & 1) { + printer_state.motorX++; + printer_state.motorY++; + } else { + printer_state.motorX--; + printer_state.motorY--; + } +#endif +#if DRIVE_SYSTEM==2 + if(cur->dir & 1) { + printer_state.motorX++; + printer_state.motorY--; + } else { + printer_state.motorX--; + printer_state.motorY++; + } +#endif +#endif // XY_GANTRY + cur->error[0] += cur_errupd; +#ifdef DEBUG_STEPCOUNT + cur->totalStepsRemaining--; +#endif + } + } + if(cur->dir & 32) { + if((cur->error[1] -= cur->delta[1]) < 0) { + ANALYZER_ON(ANALYZER_CH7); +#if DRIVE_SYSTEM==0 || !defined(XY_GANTRY) + ANALYZER_ON(ANALYZER_CH3); + WRITE(Y_STEP_PIN,HIGH); +#else +#if DRIVE_SYSTEM==1 + if(cur->dir & 2) { + printer_state.motorX++; + printer_state.motorY--; + } else { + printer_state.motorX--; + printer_state.motorY++; + } +#endif +#if DRIVE_SYSTEM==2 + if(cur->dir & 2) { + printer_state.motorX++; + printer_state.motorY++; + } else { + printer_state.motorX--; + printer_state.motorY--; + } +#endif +#endif // XY_GANTRY + cur->error[1] += cur_errupd; +#ifdef DEBUG_STEPCOUNT + cur->totalStepsRemaining--; +#endif + } + } +#if defined(XY_GANTRY) + if(printer_state.motorX <= -2) { + ANALYZER_ON(ANALYZER_CH2); + WRITE(X_STEP_PIN,HIGH); + printer_state.motorX += 2; + } else if(printer_state.motorX >= 2) { + ANALYZER_ON(ANALYZER_CH2); + WRITE(X_STEP_PIN,HIGH); + printer_state.motorX -= 2; + } + if(printer_state.motorY <= -2) { + ANALYZER_ON(ANALYZER_CH3); + WRITE(Y_STEP_PIN,HIGH); + printer_state.motorY += 2; + } else if(printer_state.motorY >= 2) { + ANALYZER_ON(ANALYZER_CH3); + WRITE(Y_STEP_PIN,HIGH); + printer_state.motorY -= 2; + } + +#endif + + if(cur->dir & 64) { + if((cur->error[2] -= cur->delta[2]) < 0) { + WRITE(Z_STEP_PIN,HIGH); + cur->error[2] += cur_errupd; +#ifdef DEBUG_STEPCOUNT + cur->totalStepsRemaining--; +#endif + } + } +#if STEPPER_HIGH_DELAY>0 + delayMicroseconds(STEPPER_HIGH_DELAY); +#endif +#if USE_OPS==1 || defined(USE_ADVANCE) + if((printer_state.flag0 & PRINTER_FLAG0_SEPERATE_EXTRUDER_INT)==0) // Use interrupt for movement +#endif + extruder_unstep(); + WRITE(X_STEP_PIN,LOW); + WRITE(Y_STEP_PIN,LOW); + WRITE(Z_STEP_PIN,LOW); + ANALYZER_OFF(ANALYZER_CH1); + ANALYZER_OFF(ANALYZER_CH2); + ANALYZER_OFF(ANALYZER_CH3); + ANALYZER_OFF(ANALYZER_CH6); + ANALYZER_OFF(ANALYZER_CH7); + } // for loop + if(do_odd) { + sei(); // Allow interrupts for other types, timer1 is still disabled +#ifdef RAMP_ACCELERATION + //If acceleration is enabled on this move and we are in the acceleration segment, calculate the current interval + if (printer_state.stepNumber <= cur->accelSteps) { // we are accelerating + printer_state.vMaxReached = ComputeV(printer_state.timer,cur->facceleration)+cur->vStart; + if(printer_state.vMaxReached>cur->vMax) printer_state.vMaxReached = cur->vMax; + unsigned int v; + if(printer_state.vMaxReached>STEP_DOUBLER_FREQUENCY) { + #if ALLOW_QUADSTEPPING + if(printer_state.vMaxReached>STEP_DOUBLER_FREQUENCY*2) { + printer_state.stepper_loops = 4; + v = printer_state.vMaxReached>>2; + } else { + printer_state.stepper_loops = 2; + v = printer_state.vMaxReached>>1; + } + #else + printer_state.stepper_loops = 2; + v = printer_state.vMaxReached>>1; + #endif + } else { + printer_state.stepper_loops = 1; + v = printer_state.vMaxReached; + } + printer_state.interval = CPUDivU2(v); + printer_state.timer+=printer_state.interval; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + long advance_target =printer_state.advance_executed+cur->advanceRate; + for(byte loop=1;loopadvanceRate; + if(advance_target>cur->advanceFull) + advance_target = cur->advanceFull; + cli(); + long h = mulu6xu16to32(printer_state.vMaxReached,cur->advanceL); + int tred = ((advance_target+h)>>16); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + printer_state.advance_executed = advance_target; +#else + int tred = mulu6xu16shift16(printer_state.vMaxReached,cur->advanceL); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); +#endif +#endif + } else if (cur->stepsRemaining <= cur->decelSteps) { // time to slow down + if (!(cur->flags & FLAG_DECELERATING)) { + printer_state.timer = 0; + cur->flags |= FLAG_DECELERATING; + } + unsigned int v = ComputeV(printer_state.timer,cur->facceleration); + if (v > printer_state.vMaxReached) // if deceleration goes too far it can become too large + v = cur->vEnd; + else{ + v=printer_state.vMaxReached-v; + if (vvEnd) v = cur->vEnd; // extra steps at the end of desceleration due to rounding erros + } +#ifdef USE_ADVANCE + unsigned int v0 = v; +#endif + if(v>STEP_DOUBLER_FREQUENCY) { +#if ALLOW_QUADSTEPPING + if(v>STEP_DOUBLER_FREQUENCY*2) { + printer_state.stepper_loops = 4; + v = v>>2; + } else { + printer_state.stepper_loops = 2; + v = v>>1; + } +#else + printer_state.stepper_loops = 2; + v = v>>1; +#endif + } else { + printer_state.stepper_loops = 1; + } + printer_state.interval = CPUDivU2(v); + printer_state.timer+=printer_state.interval; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + long advance_target =printer_state.advance_executed-cur->advanceRate; + for(byte loop=1;loopadvanceRate; + if(advance_targetadvanceEnd) + advance_target = cur->advanceEnd; + long h=mulu6xu16to32(cur->advanceL,v0); + int tred = ((advance_target+h)>>16); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); + printer_state.advance_executed = advance_target; +#else + int tred=mulu6xu16shift16(cur->advanceL,v0); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); +#endif +#endif + } else { + // If we had acceleration, we need to use the latest vMaxReached and interval + // If we started full speed, we need to use cur->fullInterval and vMax +#ifdef USE_ADVANCE + unsigned int v; + if(!cur->accelSteps) { + v = cur->vMax; + } else { + v = printer_state.vMaxReached; + } +#ifdef ENABLE_QUADRATIC_ADVANCE + long h=mulu6xu16to32(cur->advanceL,v); + int tred = ((printer_state.advance_executed+h)>>16); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); +#else + int tred=mulu6xu16shift16(cur->advanceL,v); + cli(); + printer_state.extruderStepsNeeded+=tred-printer_state.advance_steps_set; + printer_state.advance_steps_set = tred; + sei(); +#endif +#endif + if(!cur->accelSteps) { + if(cur->vMax>STEP_DOUBLER_FREQUENCY) { +#if ALLOW_QUADSTEPPING + if(cur->vMax>STEP_DOUBLER_FREQUENCY*2) { + printer_state.stepper_loops = 4; + printer_state.interval = cur->fullInterval>>2; + } else { + printer_state.stepper_loops = 2; + printer_state.interval = cur->fullInterval>>1; + } +#else + printer_state.stepper_loops = 2; + printer_state.interval = cur->fullInterval>>1; +#endif + } else { + printer_state.stepper_loops = 1; + printer_state.interval = cur->fullInterval; + } + } + } +#else + printer_state.interval = cur->fullInterval; // without RAMPS always use full speed +#endif + } // do_odd + if(do_even) { + printer_state.stepNumber+=max_loops; + cur->stepsRemaining-=max_loops; + } + +#if USE_OPS==1 + if(printer_state.opsMode==2 && (cur->joinFlags & FLAG_JOIN_END_RETRACT) && printer_state.filamentRetracted && cur->stepsRemaining<=cur->opsReverseSteps) { +#ifdef DEBUG_OPS + OUT_P_L_LN("DownX",cur->stepsRemaining); +#endif + // Point for retraction reversal reached. + printer_state.filamentRetracted = false; + cli(); + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; +#ifdef DEBUG_OPS + sei(); + OUT_P_L_LN("N=",printer_state.extruderStepsNeeded); +#endif + } +#endif + } // stepsRemaining + long interval; + if(cur->halfstep) interval = (printer_state.interval>>1); // time to come back + else interval = printer_state.interval; + if(do_even) { + if(cur->stepsRemaining<=0 || (cur->dir & 240)==0) { // line finished +#ifdef DEBUG_STEPCOUNT + if(cur->totalStepsRemaining) + OUT_P_L_LN("Missed steps:",cur->totalStepsRemaining); +#endif + +#if USE_OPS==1 + if(cur->joinFlags & FLAG_JOIN_END_RETRACT) { // Make sure filament is pushed back + sei(); + if(printer_state.filamentRetracted) { +#ifdef DEBUG_OPS + out.println_P(PSTR("Down")); +#endif + printer_state.filamentRetracted = false; + cli(); + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + } + cli(); + if(printer_state.extruderStepsNeeded) { +#ifdef DEBUG_OPS + // sei(); + // out.println_int_P(PSTR("W"),printer_state.extruderStepsNeeded); +#endif + return 4000; // wait, work is done in other interrupt + } +#ifdef DEBUG_OPS + sei(); +#endif + } +#endif + cli(); + NEXT_PLANNER_INDEX(lines_pos); + cur = 0; + --lines_count; +#ifdef XY_GANTRY + if(DISABLE_X && DISABLE_Y) { + disable_x(); + disable_y(); + } +#else + if(DISABLE_X) disable_x(); + if(DISABLE_Y) disable_y(); +#endif + if(DISABLE_Z) disable_z(); + if(lines_count==0) UI_STATUS(UI_TEXT_IDLE); + interval = printer_state.interval = interval>>1; // 50% of time to next call to do cur=0 + } + DEBUG_MEMORY; + } // Do even + return interval; +} +#endif +void(* resetFunc) (void) = 0; //declare reset function @ address 0 +/** + \brief Stop heater and stepper motors. Disable power,if possible. +*/ +void kill(byte only_steppers) +{ + if((printer_state.flag0 & PRINTER_FLAG0_STEPPER_DISABLED) && only_steppers) return; + printer_state.flag0 |=PRINTER_FLAG0_STEPPER_DISABLED; + disable_x(); + disable_y(); + disable_z(); + extruder_disable(); + if(!only_steppers) { + for(byte i=0;i-1 + //pinMode(PS_ON_PIN,INPUT); + SET_OUTPUT(PS_ON_PIN); //GND + WRITE(PS_ON_PIN, HIGH); +#endif + } else UI_STATUS_UPD(UI_TEXT_STEPPER_DISABLED); +} +long stepperWait = 0; +/** \brief Sets the timer 1 compare value to delay ticks. + +This function sets the OCR1A compare counter to get the next interrupt +at delay ticks measured from the last interrupt. delay must be << 2^24 +*/ +inline void setTimer(unsigned long delay) +{ + __asm__ __volatile__ ( + "cli \n\t" + "tst %C[delay] \n\t" //if(delay<65536) { + "brne else%= \n\t" + "cpi %B[delay],255 \n\t" + "breq else%= \n\t" // delay <65280 + "sts stepperWait,r1 \n\t" // stepperWait = 0; + "sts stepperWait+1,r1 \n\t" + "sts stepperWait+2,r1 \n\t" + "lds %C[delay],%[time] \n\t" // Read TCNT1 + "lds %D[delay],%[time]+1 \n\t" + "ldi r18,100 \n\t" // Add 100 to TCNT1 + "add %C[delay],r18 \n\t" + "adc %D[delay],r1 \n\t" + "cp %A[delay],%C[delay] \n\t" // delay 65535 + "lds r23,stepperWait+1 \n\t" + "tst r23 \n\t" + "brne last%= \n\t" // Still not 0, go ahead + "lds r22,stepperWait \n\t" + "breq end%= \n\t" // stepperWait is 0, do your work + "last%=: \n\t" + "sts %[ocr]+1,r23 \n\t" // OCR1A = stepper wait; + "sts %[ocr],r22 \n\t" + "sts stepperWait,r1 \n\t" + "sts stepperWait+1,r1 \n\t" + "rjmp end1%= \n\t" + "else%=: lds r22,stepperWait+1 \n\t" //} else { stepperWait = stepperWait-32768; + "subi r22, 0x80 \n\t" + "sbci r23, 0x00 \n\t" + "sts stepperWait+1,r22 \n\t" // ocr1a stays 32768 + "sts stepperWait+2,r23 \n\t" + "end1%=: ldi %[ex],1 \n\t" + "end%=: \n\t" + :[ex]"=&d"(doExit):[ocr]"i" (_SFR_MEM_ADDR(OCR1A)):"r22","r23" ); + if(doExit) return; + insideTimer1=1; + OCR1A=61000; + if(lines_count) { + setTimer(bresenham_step()); + } else { + if(waitRelax==0) { +#if USE_OPS==1 + // push filament in normal position if printings stops, so we have a defined starting position + if(printer_state.opsMode && printer_state.filamentRetracted) { +#ifdef DEBUG_OPS + out.println_P(PSTR("DownI")); +#endif + printer_state.extruderStepsNeeded+=printer_state.opsPushbackSteps; + printer_state.filamentRetracted = false; + } + printmoveSeen = 0; +#endif +#ifdef USE_ADVANCE + if(printer_state.advance_steps_set) { + printer_state.extruderStepsNeeded-=printer_state.advance_steps_set; +#ifdef ENABLE_QUADRATIC_ADVANCE + printer_state.advance_executed = 0; +#endif + printer_state.advance_steps_set = 0; + } +#endif +#if USE_OPS==1 || defined(USE_ADVANCE) + if(!printer_state.extruderStepsNeeded) if(DISABLE_E) extruder_disable(); +#else + if(DISABLE_E) extruder_disable(); +#endif + } else waitRelax--; + stepperWait = 0; // Importent becaus of optimization in asm at begin + OCR1A = 65500; // Wait for next move + } + DEBUG_MEMORY; + insideTimer1=0; +} + +#if USE_OPS==1 || defined(USE_ADVANCE) +byte extruder_wait_dirchange=0; ///< Wait cycles, if direction changes. Prevents stepper from loosing steps. +char extruder_last_dir = 0; +byte extruder_speed = 0; +#endif + + +/** \brief Timer routine for extruder stepper. + +Several methods need to move the extruder. To get a optima result, +all methods update the printer_state.extruderStepsNeeded with the +number of additional steps needed. During this interrupt, one step +is executed. This will keep the extruder moving, until the total +wanted movement is achieved. This will be done with the maximum +allowable speed for the extruder. +*/ +ISR(EXTRUDER_TIMER_VECTOR) +{ +#if USE_OPS==1 || defined(USE_ADVANCE) + if((printer_state.flag0 & PRINTER_FLAG0_SEPERATE_EXTRUDER_INT)==0) return; // currently no need + byte timer = EXTRUDER_OCR; + bool increasing = printer_state.extruderStepsNeeded>0; + + if(printer_state.extruderStepsNeeded==0) { + extruder_last_dir = 0; + } else if((increasing>0 && extruder_last_dir<0) || (!increasing && extruder_last_dir>0)) { + EXTRUDER_OCR = timer+50; // Little delay to accomodate to reversed direction + extruder_set_direction(increasing ? 1 : 0); + extruder_last_dir = (increasing ? 1 : -1); + return; + } else { + if(extruder_last_dir==0) { + extruder_set_direction(increasing ? 1 : 0); + extruder_last_dir = (increasing ? 1 : -1); + } + extruder_step(); + printer_state.extruderStepsNeeded-=extruder_last_dir; +#if STEPPER_HIGH_DELAY>0 + delayMicroseconds(STEPPER_HIGH_DELAY); +#endif + extruder_unstep(); + } + EXTRUDER_OCR = timer+printer_state.maxExtruderSpeed; + +/* Version of 0.7 branch + static byte accdelay=10; + if(printer_state.extruderStepsNeeded==0) { + extruder_last_dir = 0; + } else if((increasing>0 && extruder_last_dir<0) || (!increasing && extruder_last_dir>0)) { + EXTRUDER_OCR = timer+50; // Little delay to accomodate to reversed direction + extruder_set_direction(increasing ? 1 : 0); + extruder_last_dir = (increasing ? 1 : -1); + return; + } else { + if(extruder_last_dir==0) { + extruder_set_direction(increasing ? 1 : 0); + extruder_last_dir = (increasing ? 1 : -1); + } + extruder_step(); + printer_state.extruderStepsNeeded-=extruder_last_dir; +#if STEPPER_HIGH_DELAY>0 + delayMicroseconds(STEPPER_HIGH_DELAY); +#endif + extruder_unstep(); + } + EXTRUDER_OCR = timer+printer_state.maxExtruderSpeed; + } + */ +/* + EXTRUDER_OCR += printer_state.timer0Interval; // time to come back + // The stepper signals are in strategical positions for optimal timing. If you + // still have timing issues, add dummy commands between. + if(printer_state.extruderStepsNeeded) { + extruder_unstep(); + if(printer_state.extruderStepsNeeded<0) { // Backward step + extruder_set_direction(0); + if(extruder_wait_dirchange && extruder_last_dir==-1) { + extruder_wait_dirchange--; + return; + } + extruder_last_dir = 1; + extruder_wait_dirchange=2; + printer_state.extruderStepsNeeded++; + } else { // Forward step + extruder_set_direction(1); + if(extruder_wait_dirchange && extruder_last_dir==1) { + extruder_wait_dirchange--; + return; + } + extruder_last_dir = -1; + extruder_wait_dirchange=2; + printer_state.extruderStepsNeeded--; + } + if(current_extruder->currentTemperatureC>=MIN_EXTRUDER_TEMP<-1 + if((pwm_pos_set[0] = pwm_pos[0])>0) WRITE(EXT0_HEATER_PIN,1); +#if EXT0_EXTRUDER_COOLER_PIN>-1 + if((pwm_cooler_pos_set[0] = extruder[0].coolerPWM)>0) WRITE(EXT0_EXTRUDER_COOLER_PIN,1); +#endif +#endif +#if defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN>-1 + if((pwm_pos_set[1] = pwm_pos[1])>0) WRITE(EXT1_HEATER_PIN,1); +#if EXT1_EXTRUDER_COOLER_PIN>-1 + if((pwm_cooler_pos_set[1] = extruder[1].coolerPWM)>0) WRITE(EXT1_EXTRUDER_COOLER_PIN,1); +#endif +#endif +#if defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN>-1 + if((pwm_pos_set[2] = pwm_pos[2])>0) WRITE(EXT2_HEATER_PIN,1); +#if EXT2_EXTRUDER_COOLER_PIN>-1 + if((pwm_cooler_pos_set[2] = extruder[2].coolerPWM)>0) WRITE(EXT2_EXTRUDER_COOLER_PIN,1); +#endif +#endif +#if defined(EXT3_HEATER_PIN) && EXT3_HEATER_PIN>-1 + if((pwm_pos_set[3] = pwm_pos[3])>0) WRITE(EXT3_HEATER_PIN,1); +#if EXT3_EXTRUDER_COOLER_PIN>-1 + if((pwm_cooler_pos_set[3] = extruder[3].coolerPWM)>0) WRITE(EXT3_EXTRUDER_COOLER_PIN,1); +#endif +#endif +#if defined(EXT4_HEATER_PIN) && EXT4_HEATER_PIN>-1 + if((pwm_pos_set[4] = pwm_pos[4])>0) WRITE(EXT4_HEATER_PIN,1); +#if EXT4_EXTRUDER_COOLER_PIN>-1 + if((pwm_cooler_pos_set[4] = pwm_pos[4].coolerPWM)>0) WRITE(EXT4_EXTRUDER_COOLER_PIN,1); +#endif +#endif +#if defined(EXT5_HEATER_PIN) && EXT5_HEATER_PIN>-1 + if((pwm_pos_set[5] = pwm_pos[5])>0) WRITE(EXT5_HEATER_PIN,1); +#if EXT5_EXTRUDER_COOLER_PIN>-1 + if((pwm_cooler_pos_set[5] = extruder[5].coolerPWM)>0) WRITE(EXT5_EXTRUDER_COOLER_PIN,1); +#endif +#endif +#if FAN_BOARD_PIN>-1 + if((pwm_pos_set[NUM_EXTRUDER+1] = pwm_pos[NUM_EXTRUDER+1])>0) WRITE(FAN_BOARD_PIN,1); +#endif +#if FAN_PIN>-1 && FEATURE_FAN_CONTROL + if((pwm_pos_set[NUM_EXTRUDER+2] = pwm_pos[NUM_EXTRUDER+2])>0) WRITE(FAN_PIN,1); +#endif +#if HEATED_BED_HEATER_PIN>-1 && HAVE_HEATED_BED + if((pwm_pos_set[NUM_EXTRUDER] = pwm_pos[NUM_EXTRUDER])>0) WRITE(HEATED_BED_HEATER_PIN,1); +#endif + } +#if EXT0_HEATER_PIN>-1 + if(pwm_pos_set[0] == pwm_count && pwm_pos_set[0]!=255) WRITE(EXT0_HEATER_PIN,0); +#if EXT0_EXTRUDER_COOLER_PIN>-1 + if(pwm_cooler_pos_set[0] == pwm_count && pwm_cooler_pos_set[0]!=255) WRITE(EXT0_EXTRUDER_COOLER_PIN,0); +#endif +#endif +#if defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN>-1 + if(pwm_pos_set[1] == pwm_count && pwm_pos_set[1]!=255) WRITE(EXT1_HEATER_PIN,0); +#if EXT1_EXTRUDER_COOLER_PIN>-1 + if(pwm_cooler_pos_set[1] == pwm_count && pwm_cooler_pos_set[1]!=255) WRITE(EXT1_EXTRUDER_COOLER_PIN,0); +#endif +#endif +#if defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN>-1 + if(pwm_pos_set[2] == pwm_count && pwm_pos_set[2]!=255) WRITE(EXT2_HEATER_PIN,0); +#if EXT2_EXTRUDER_COOLER_PIN>-1 + if(pwm_cooler_pos_set[2] == pwm_count && pwm_cooler_pos_set[2]!=255) WRITE(EXT2_EXTRUDER_COOLER_PIN,0); +#endif +#endif +#if defined(EXT3_HEATER_PIN) && EXT3_HEATER_PIN>-1 + if(pwm_pos_set[3] == pwm_count && pwm_pos_set[3]!=255) WRITE(EXT3_HEATER_PIN,0); +#if EXT3_EXTRUDER_COOLER_PIN>-1 + if(pwm_cooler_pos_set[3] == pwm_count && pwm_cooler_pos_set[3]!=255) WRITE(EXT3_EXTRUDER_COOLER_PIN,0); +#endif +#endif +#if defined(EXT4_HEATER_PIN) && EXT4_HEATER_PIN>-1 + if(pwm_pos_set[4] == pwm_count && pwm_pos_set[4]!=255) WRITE(EXT4_HEATER_PIN,0); +#if EXT4_EXTRUDER_COOLER_PIN>-1 + if(pwm_cooler_pos_set[4] == pwm_count && pwm_cooler_pos_set[4]!=255) WRITE(EXT4_EXTRUDER_COOLER_PIN,0); +#endif +#endif +#if defined(EXT5_HEATER_PIN) && EXT5_HEATER_PIN>-1 + if(pwm_pos_set[5] == pwm_count && pwm_pos_set[5]!=255) WRITE(EXT5_HEATER_PIN,0); +#if EXT5_EXTRUDER_COOLER_PIN>-1 + if(pwm_cooler_pos_set[5] == pwm_count && pwm_cooler_pos_set[5]!=255) WRITE(EXT5_EXTRUDER_COOLER_PIN,0); +#endif +#endif +#if FAN_BOARD_PIN>-1 + if(pwm_pos_set[NUM_EXTRUDER+2] == pwm_count && pwm_pos_set[NUM_EXTRUDER+2]!=255) WRITE(FAN_BOARD_PIN,0); +#endif +#if FAN_PIN>-1 && FEATURE_FAN_CONTROL + if(pwm_pos_set[NUM_EXTRUDER+2] == pwm_count && pwm_pos_set[NUM_EXTRUDER+2]!=255) WRITE(FAN_PIN,0); +#endif +#if HEATED_BED_HEATER_PIN>-1 && HAVE_HEATED_BED + if(pwm_pos_set[NUM_EXTRUDER] == pwm_count && pwm_pos_set[NUM_EXTRUDER]!=255) WRITE(HEATED_BED_HEATER_PIN,0); +#endif + sei(); + counter_periodical++; // Appxoimate a 100ms timer + if(counter_periodical>=(int)(F_CPU/40960)) { + counter_periodical=0; + execute_periodical=1; + } +// read analog values +#if ANALOG_INPUTS>0 +if((ADCSRA & _BV(ADSC))==0) { // Conversion finished? + osAnalogInputBuildup[osAnalogInputPos] += ADCW; + if(++osAnalogInputCounter[osAnalogInputPos]>=_BV(ANALOG_INPUT_SAMPLE)) { +#if ANALOG_INPUT_BITS+ANALOG_INPUT_SAMPLE<12 + osAnalogInputValues[osAnalogInputPos] = + osAnalogInputBuildup[osAnalogInputPos] << + (12-ANALOG_INPUT_BITS-ANALOG_INPUT_SAMPLE); +#endif +#if ANALOG_INPUT_BITS+ANALOG_INPUT_SAMPLE>12 + osAnalogInputValues[osAnalogInputPos] = + osAnalogInputBuildup[osAnalogInputPos] >> + (ANALOG_INPUT_BITS+ANALOG_INPUT_SAMPLE-12); +#endif +#if ANALOG_INPUT_BITS+ANALOG_INPUT_SAMPLE==12 + osAnalogInputValues[osAnalogInputPos] = + osAnalogInputBuildup[osAnalogInputPos]; +#endif + osAnalogInputBuildup[osAnalogInputPos] = 0; + osAnalogInputCounter[osAnalogInputPos] = 0; + // Start next conversion + if(++osAnalogInputPos>=ANALOG_INPUTS) osAnalogInputPos = 0; + byte channel = pgm_read_byte(&osAnalogInputChannels[osAnalogInputPos]); +#if defined(ADCSRB) && defined(MUX5) + if(channel & 8) // Reading channel 0-7 or 8-15? + ADCSRB |= _BV(MUX5); + else + ADCSRB &= ~_BV(MUX5); +#endif + ADMUX = (ADMUX & ~(0x1F)) | (channel & 7); + } + ADCSRA |= _BV(ADSC); // start next conversion + } +#endif + + UI_FAST; // Short timed user interface action + pwm_count++; +} + + diff --git a/Repetier/Reptier.h b/Repetier/Reptier.h new file mode 100644 index 0000000..050c69a --- /dev/null +++ b/Repetier/Reptier.h @@ -0,0 +1,983 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. +*/ + +#ifndef _REPETIER_H +#define _REPETIER_H + +#include + +#define REPETIER_VERSION "0.80" + +// ########################################################################################## +// ## Debug configuration ## +// ########################################################################################## + +/** Uncomment, to see detailed data for every move. Only for debugging purposes! */ +//#define DEBUG_QUEUE_MOVE +/** Allows M111 to set bit 5 (16) which disables all commands except M111. This can be used +to test your data througput or search for communication problems. */ +#define INCLUDE_DEBUG_COMMUNICATION +/** Allows M111 so set bit 6 (32) which disables moves, at the first tried step. In combination +with a dry run, you can test the speed of path computations, which are still performed. */ +//#define INCLUDE_DEBUG_NO_MOVE +/** Writes the free RAM to output, if it is less then at the last test. Should always return +values >500 for safety, since it doesn't catch every function call. Nice to tweak cache +usage or for seraching for memory induced errors. Switch it off for production, it costs execution time. */ +//#define DEBUG_FREE_MEMORY +//#define DEBUG_ADVANCE +/** \brief print ops related debug info. */ +//#define DEBUG_OPS +/** If enabled, writes the created generic table to serial port at startup. */ +//#define DEBUG_GENERIC +/** If enabled, steps to move and moved steps are compared. */ +//#define DEBUG_STEPCOUNT +// Uncomment the following line to enable debugging. You can better control debugging below the following line +//#define DEBUG + + +// Uncomment if no analyzer is connected +//#define ANALYZER +// Channel->pin assignments +#define ANALYZER_CH0 63 // New move +#define ANALYZER_CH1 40 // Step loop +#define ANALYZER_CH2 53 // X Step +#define ANALYZER_CH3 65 // Y Step +#define ANALYZER_CH4 59 // X Direction +#define ANALYZER_CH5 64 // Y Direction +#define ANALYZER_CH6 58 // xsig +#define ANALYZER_CH7 57 // ysig + +#ifdef ANALYZER +#define ANALYZER_ON(a) {WRITE(a,HIGH);} +#define ANALYZER_OFF(a) {WRITE(a,LOW);} +#else +#define ANALYZER_ON(a) +#define ANALYZER_OFF(a) +#endif + + +// Bits of the ADC converter +#define ANALOG_INPUT_BITS 10 +// Build median from 2^ANALOG_INPUT_SAMPLE samples +#define ANALOG_INPUT_SAMPLE 5 +#define ANALOG_REF_AREF 0 +#define ANALOG_REF_AVCC _BV(REFS0) +#define ANALOG_REF_INT_1_1 _BV(REFS1) +#define ANALOG_REF_INT_2_56 _BV(REFS0) | _BV(REFS1) + +// MS1 MS2 Stepper Driver Microstepping mode table +#define MICROSTEP1 LOW,LOW +#define MICROSTEP2 HIGH,LOW +#define MICROSTEP4 LOW,HIGH +#define MICROSTEP8 HIGH,HIGH +#define MICROSTEP16 HIGH,HIGH + +#define NEW_XY_GANTRY + +#include "Configuration.h" +#if DRIVE_SYSTEM==1 || DRIVE_SYSTEM==2 +#define XY_GANTRY +#endif + +//Step to split a cirrcle in small Lines +#ifndef MM_PER_ARC_SEGMENT +#define MM_PER_ARC_SEGMENT 1 +#define MM_PER_ARC_SEGMENT_BIG 3 +#else +#define MM_PER_ARC_SEGMENT_BIG MM_PER_ARC_SEGMENT +#endif +//After this count of steps a new SIN / COS caluclation is startet to correct the circle interpolation +#define N_ARC_CORRECTION 25 +#if CPU_ARCH==ARCH_AVR +#include +#else +#define PROGMEM +#define PGM_P const char * +#define PSTR(s) s +#define pgm_read_byte_near(x) (*(char*)x) +#define pgm_read_byte(x) (*(char*)x) +#endif + +#define KOMMA +#if NUM_EXTRUDER>0 && EXT0_TEMPSENSOR_TYPE<100 +#define EXT0_ANALOG_INPUTS 1 +#define EXT0_SENSOR_INDEX 0 +#define EXT0_ANALOG_CHANNEL EXT0_TEMPSENSOR_PIN +#undef KOMMA +#define KOMMA , +#else +#define EXT0_ANALOG_INPUTS 0 +#define EXT0_SENSOR_INDEX EXT0_TEMPSENSOR_PIN +#define EXT0_ANALOG_CHANNEL +#endif + +#if NUM_EXTRUDER>1 && EXT1_TEMPSENSOR_TYPE<100 +#define EXT1_ANALOG_INPUTS 1 +#define EXT1_SENSOR_INDEX EXT0_ANALOG_INPUTS +#define EXT1_ANALOG_CHANNEL KOMMA EXT1_TEMPSENSOR_PIN +#undef KOMMA +#define KOMMA , +#else +#define EXT1_ANALOG_INPUTS 0 +#define EXT1_SENSOR_INDEX EXT1_TEMPSENSOR_PIN +#define EXT1_ANALOG_CHANNEL +#endif + +#if NUM_EXTRUDER>2 && EXT2_TEMPSENSOR_TYPE<100 +#define EXT2_ANALOG_INPUTS 1 +#define EXT2_SENSOR_INDEX EXT0_ANALOG_INPUTS+EXT1_ANALOG_INPUTS +#define EXT2_ANALOG_CHANNEL KOMMA EXT2_TEMPSENSOR_PIN +#undef KOMMA +#define KOMMA , +#else +#define EXT2_ANALOG_INPUTS 0 +#define EXT2_SENSOR_INDEX EXT2_TEMPSENSOR_PIN +#define EXT2_ANALOG_CHANNEL +#endif + +#if NUM_EXTRUDER>3 && EXT3_TEMPSENSOR_TYPE<100 +#define EXT3_ANALOG_INPUTS 1 +#define EXT3_SENSOR_INDEX EXT0_ANALOG_INPUTS+EXT1_ANALOG_INPUTS+EXT2_ANALOG_INPUTS +#define EXT3_ANALOG_CHANNEL KOMMA EXT3_TEMPSENSOR_PIN +#undef KOMMA +#define KOMMA , +#else +#define EXT3_ANALOG_INPUTS 0 +#define EXT3_SENSOR_INDEX EXT3_TEMPSENSOR_PIN +#define EXT3_ANALOG_CHANNEL +#endif + +#if NUM_EXTRUDER>4 && EXT4_TEMPSENSOR_TYPE<100 +#define EXT4_ANALOG_INPUTS 1 +#define EXT4_SENSOR_INDEX EXT0_ANALOG_INPUTS+EXT1_ANALOG_INPUTS+EXT2_ANALOG_INPUTS+EXT3_ANALOG_INPUTS +#define EXT4_ANALOG_CHANNEL KOMMA EXT4_TEMPSENSOR_PIN +#undef KOMMA +#define KOMMA , +#else +#define EXT4_ANALOG_INPUTS 0 +#define EXT4_SENSOR_INDEX EXT4_TEMPSENSOR_PIN +#define EXT4_ANALOG_CHANNEL +#endif + +#if NUM_EXTRUDER>5 && EXT5_TEMPSENSOR_TYPE<100 +#define EXT5_ANALOG_INPUTS 1 +#define EXT5_SENSOR_INDEX EXT0_ANALOG_INPUTS+EXT1_ANALOG_INPUTS+EXT2_ANALOG_INPUTS+EXT3_ANALOG_INPUTS+EXT4_ANALOG_INPUTS +#define EXT5_ANALOG_CHANNEL KOMMA EXT5_TEMPSENSOR_PIN +#undef KOMMA +#define KOMMA , +#else +#define EXT5_ANALOG_INPUTS 0 +#define EXT5_SENSOR_INDEX EXT5_TEMPSENSOR_PIN +#define EXT5_ANALOG_CHANNEL +#endif + +#if HAVE_HEATED_BED==true && HEATED_BED_SENSOR_TYPE<100 +#define BED_ANALOG_INPUTS 1 +#define BED_SENSOR_INDEX EXT0_ANALOG_INPUTS+EXT1_ANALOG_INPUTS+EXT2_ANALOG_INPUTS+EXT3_ANALOG_INPUTS+EXT4_ANALOG_INPUTS+EXT5_ANALOG_INPUTS +#define BED_ANALOG_CHANNEL KOMMA HEATED_BED_SENSOR_PIN +#undef KOMMA +#define KOMMA , +#else +#define BED_ANALOG_INPUTS 0 +#define BED_SENSOR_INDEX HEATED_BED_SENSOR_PIN +#define BED_ANALOG_CHANNEL +#endif + +#ifndef DEBUG_FREE_MEMORY +#define DEBUG_MEMORY +#else +#define DEBUG_MEMORY check_mem(); +#endif + +/** \brief number of analog input signals. Normally 1 for each temperature sensor */ +#define ANALOG_INPUTS (EXT0_ANALOG_INPUTS+EXT1_ANALOG_INPUTS+EXT2_ANALOG_INPUTS+EXT3_ANALOG_INPUTS+EXT4_ANALOG_INPUTS+EXT5_ANALOG_INPUTS+BED_ANALOG_INPUTS) +#if ANALOG_INPUTS>0 +/** Channels are the MUX-part of ADMUX register */ +#define ANALOG_INPUT_CHANNELS {EXT0_ANALOG_CHANNEL EXT1_ANALOG_CHANNEL EXT2_ANALOG_CHANNEL EXT3_ANALOG_CHANNEL EXT4_ANALOG_CHANNEL EXT5_ANALOG_CHANNEL BED_ANALOG_CHANNEL} +#endif +#define ANALOG_PRESCALER _BV(ADPS0)|_BV(ADPS1)|_BV(ADPS2) + +#if MOTHERBOARD==8 || MOTHERBOARD==9 || CPU_ARCH!=ARCH_AVR +#define EXTERNALSERIAL +#endif +//#define EXTERNALSERIAL // Force using arduino serial +#ifndef EXTERNALSERIAL +#define HardwareSerial_h // Don't use standard serial console +#endif +#include +#include "Print.h" + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#define COMPAT_PRE1 +#endif +#include "gcode.h" +#if CPU_ARCH==ARCH_AVR +#include "fastio.h" +#else +#define READ(IO) digitalRead(IO) +#define WRITE(IO, v) digitalWrite(IO, v) +#define SET_INPUT(IO) pinMode(IO, INPUT) +#define SET_OUTPUT(IO) pinMode(IO, OUTPUT) +#endif +#define SD_MAX_FOLDER_DEPTH 2 + +#include "ui.h" + +#ifndef SDSUPPORT +#define SDSUPPORT false +#endif +#if SDSUPPORT +#include "SdFat.h" +#endif +#ifndef SDSUPPORT +#define SDSUPPORT false +#endif +#if SDSUPPORT +#include "SdFat.h" +#endif + +#if ENABLE_BACKLASH_COMPENSATION && DRIVE_SYSTEM!=0 +#undef ENABLE_BACKLASH_COMPENSATION +#define ENABLE_BACKLASH_COMPENSATION false +#endif + +#define uint uint16_t +#define uint8 uint8_t +#define int8 int8_t +#define uint32 uint32_t +#define int32 int32_t + +/*#if MOTHERBOARD==6 || MOTHERBOARD==62 || MOTHERBOARD==7 +#if MOTHERBOARD!=7 +#define SIMULATE_PWM +#endif +#define EXTRUDER_TIMER_VECTOR TIMER2_COMPA_vect +#define EXTRUDER_OCR OCR2A +#define EXTRUDER_TCCR TCCR2A +#define EXTRUDER_TIMSK TIMSK2 +#define EXTRUDER_OCIE OCIE2A +#define PWM_TIMER_VECTOR TIMER2_COMPB_vect +#define PWM_OCR OCR2B +#define PWM_TCCR TCCR2B +#define PWM_TIMSK TIMSK2 +#define PWM_OCIE OCIE2B +#else*/ +#define EXTRUDER_TIMER_VECTOR TIMER0_COMPA_vect +#define EXTRUDER_OCR OCR0A +#define EXTRUDER_TCCR TCCR0A +#define EXTRUDER_TIMSK TIMSK0 +#define EXTRUDER_OCIE OCIE0A +#define PWM_TIMER_VECTOR TIMER0_COMPB_vect +#define PWM_OCR OCR0B +#define PWM_TCCR TCCR0A +#define PWM_TIMSK TIMSK0 +#define PWM_OCIE OCIE0B +//#endif + +/** TemperatureController manages one heater-temperature sensore loop. You can have up to +4 loops allowing pid/bang bang for up to 3 extruder and the heated bed. + +*/ +typedef struct { + byte pwmIndex; ///< pwm index for output control. 0-2 = Extruder, 3 = Fan, 4 = Heated Bed + byte sensorType; ///< Type of temperature sensor. + byte sensorPin; ///< Pin to read extruder temperature. + int currentTemperature; ///< Currenttemperature value read from sensor. + int targetTemperature; ///< Target temperature value in units of sensor. + float currentTemperatureC; ///< Current temperature in °C. + float targetTemperatureC; ///< Target temperature in °C. + unsigned long lastTemperatureUpdate; ///< Time in millis of the last temperature update. + char heatManager; ///< How is temperature controled. 0 = on/off, 1 = PID-Control +#ifdef TEMP_PID + long tempIState; ///< Temp. var. for PID computation. + byte pidDriveMax; ///< Used for windup in PID calculation. + byte pidDriveMin; ///< Used for windup in PID calculation. + float pidPGain; ///< Pgain (proportional gain) for PID temperature control [0,01 Units]. + float pidIGain; ///< Igain (integral) for PID temperature control [0,01 Units]. + float pidDGain; ///< Dgain (damping) for PID temperature control [0,01 Units]. + byte pidMax; ///< Maximum PWM value, the heater should be set. + float tempIStateLimitMax; + float tempIStateLimitMin; + byte tempPointer; + float tempArray[4]; +#endif +} TemperatureController; +/** \brief Data to drive one extruder. + +This structure contains all definitions for an extruder and all +current state variables, like current temperature, feeder position etc. +*/ +typedef struct { // Size: 12*1 Byte+12*4 Byte+4*2Byte = 68 Byte + byte id; + long xOffset; + long yOffset; + float stepsPerMM; ///< Steps per mm. + byte enablePin; ///< Pin to enable extruder stepper motor. +// byte directionPin; ///< Pin number to assign the direction. +// byte stepPin; ///< Pin number for a step. + byte enableOn; +// byte invertDir; ///< 1 if the direction of the extruder should be inverted. + float maxFeedrate; ///< Maximum feedrate in mm/s. + float maxAcceleration; ///< Maximum acceleration in mm/s^2. + float maxStartFeedrate; ///< Maximum start feedrate in mm/s. + long extrudePosition; ///< Current extruder position in steps. + int watchPeriod; ///< Time in seconds, a M109 command will wait to stabalize temperature + int waitRetractTemperature; ///< Temperature to retract the filament when waiting for heatup + int waitRetractUnits; ///< Units to retract the filament when waiting for heatup +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + float advanceK; ///< Koefficient for advance algorithm. 0 = off +#endif + float advanceL; +#endif + TemperatureController tempControl; + const char * PROGMEM selectCommands; + const char * PROGMEM deselectCommands; + byte coolerSpeed; ///< Speed to use when enabled + byte coolerPWM; ///< current PWM setting +} Extruder; + +extern const uint8 osAnalogInputChannels[] PROGMEM; +extern uint8 osAnalogInputCounter[ANALOG_INPUTS]; +extern uint osAnalogInputBuildup[ANALOG_INPUTS]; +extern uint8 osAnalogInputPos; // Current sampling position +extern volatile uint osAnalogInputValues[ANALOG_INPUTS]; +extern byte pwm_pos[NUM_EXTRUDER+3]; // 0-NUM_EXTRUDER = Heater 0-NUM_EXTRUDER of extruder, NUM_EXTRUDER = Heated bed, NUM_EXTRUDER+1 Board fan, NUM_EXTRUDER+2 = Fan +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE +extern int maxadv; +#endif +extern int maxadv2; +extern float maxadvspeed; +#endif + +extern Extruder *current_extruder; +extern Extruder extruder[]; +// Initalize extruder and heated bed related pins +extern void initExtruder(); +extern void initHeatedBed(); +extern void updateTempControlVars(TemperatureController *tc); +extern void extruder_select(byte ext_num); +// Set current extruder position +//extern void extruder_set_position(float pos,bool relative); +// set the temperature of current extruder +extern void extruder_set_temperature(float temp_celsius,byte extr); +// Set temperature of heated bed +extern void heated_bed_set_temperature(float temp_celsius); +//extern long extruder_steps_to_position(float value,byte relative); +extern void extruder_set_direction(byte steps); +extern void extruder_disable(); +#ifdef TEMP_PID +void autotunePID(float temp,int controllerId); +#endif + +//#ifdef TEMP_PID +//extern byte current_extruder_out; +//#endif + +/** \brief Sends the high-signal to the stepper for next extruder step. + +Call this function only, if interrupts are disabled. +*/ +inline void extruder_step() { +#if NUM_EXTRUDER==1 + WRITE(EXT0_STEP_PIN,HIGH); +#else + switch(current_extruder->id) { + case 0: +#if NUM_EXTRUDER>0 + WRITE(EXT0_STEP_PIN,HIGH); +#endif + break; +#if defined(EXT1_STEP_PIN) && NUM_EXTRUDER>1 + case 1: + WRITE(EXT1_STEP_PIN,HIGH); + break; +#endif +#if defined(EXT2_STEP_PIN) && NUM_EXTRUDER>2 + case 2: + WRITE(EXT2_STEP_PIN,HIGH); + break; +#endif +#if defined(EXT3_STEP_PIN) && NUM_EXTRUDER>3 + case 3: + WRITE(EXT3_STEP_PIN,HIGH); + break; +#endif +#if defined(EXT4_STEP_PIN) && NUM_EXTRUDER>4 + case 4: + WRITE(EXT4_STEP_PIN,HIGH); + break; +#endif +#if defined(EXT5_STEP_PIN) && NUM_EXTRUDER>5 + case 5: + WRITE(EXT5_STEP_PIN,HIGH); + break; +#endif + } +#endif +} +/** \brief Sets stepper signal to low for current extruder. + +Call this function only, if interrupts are disabled. +*/ +inline void extruder_unstep() { +#if NUM_EXTRUDER==1 + WRITE(EXT0_STEP_PIN,LOW); +#else + switch(current_extruder->id) { + case 0: +#if NUM_EXTRUDER>0 + WRITE(EXT0_STEP_PIN,LOW); +#endif + break; +#if defined(EXT1_STEP_PIN) && NUM_EXTRUDER>1 + case 1: + WRITE(EXT1_STEP_PIN,LOW); + break; +#endif +#if defined(EXT2_STEP_PIN) && NUM_EXTRUDER>2 + case 2: + WRITE(EXT2_STEP_PIN,LOW); + break; +#endif +#if defined(EXT3_STEP_PIN) && NUM_EXTRUDER>3 + case 3: + WRITE(EXT3_STEP_PIN,LOW); + break; +#endif +#if defined(EXT4_STEP_PIN) && NUM_EXTRUDER>4 + case 4: + WRITE(EXT4_STEP_PIN,LOW); + break; +#endif +#if defined(EXT5_STEP_PIN) && NUM_EXTRUDER>5 + case 5: + WRITE(EXT5_STEP_PIN,LOW); + break; +#endif + } +#endif +} +/** \brief Activates the extruder stepper and sets the direction. */ +inline void extruder_set_direction(byte dir) { +#if NUM_EXTRUDER==1 + if(dir) + WRITE(EXT0_DIR_PIN,!EXT0_INVERSE); + else + WRITE(EXT0_DIR_PIN,EXT0_INVERSE); +#else + switch(current_extruder->id) { +#if NUM_EXTRUDER>0 + case 0: + if(dir) + WRITE(EXT0_DIR_PIN,!EXT0_INVERSE); + else + WRITE(EXT0_DIR_PIN,EXT0_INVERSE); + break; +#endif +#if defined(EXT1_DIR_PIN) && NUM_EXTRUDER>1 + case 1: + if(dir) + WRITE(EXT1_DIR_PIN,!EXT1_INVERSE); + else + WRITE(EXT1_DIR_PIN,EXT1_INVERSE); + break; +#endif +#if defined(EXT2_DIR_PIN) && NUM_EXTRUDER>2 + case 2: + if(dir) + WRITE(EXT2_DIR_PIN,!EXT2_INVERSE); + else + WRITE(EXT2_DIR_PIN,EXT2_INVERSE); + break; +#endif +#if defined(EXT3_DIR_PIN) && NUM_EXTRUDER>3 + case 3: + if(dir) + WRITE(EXT3_DIR_PIN,!EXT3_INVERSE); + else + WRITE(EXT3_DIR_PIN,EXT3_INVERSE); + break; +#endif +#if defined(EXT4_DIR_PIN) && NUM_EXTRUDER>4 + case 4: + if(dir) + WRITE(EXT4_DIR_PIN,!EXT4_INVERSE); + else + WRITE(EXT4_DIR_PIN,EXT4_INVERSE); + break; +#endif +#if defined(EXT5_DIR_PIN) && NUM_EXTRUDER>5 + case 5: + if(dir) + WRITE(EXT5_DIR_PIN,!EXT5_INVERSE); + else + WRITE(EXT5_DIR_PIN,EXT5_INVERSE); + break; +#endif + } +#endif +} +inline void extruder_enable() { +#if NUM_EXTRUDER==1 +#if EXT0_ENABLE_PIN>-1 + WRITE(EXT0_ENABLE_PIN,EXT0_ENABLE_ON ); +#endif +#else + if(current_extruder->enablePin > -1) + digitalWrite(current_extruder->enablePin,current_extruder->enableOn); +#endif +} +extern void(* resetFunc) (void); +// Read a temperature and return its value in °C +// this high level method supports all known methods +extern int read_raw_temperature(byte type,byte pin); +extern float heated_bed_get_temperature(); +// Convert a raw temperature value into °C +extern float conv_raw_temp(byte type,int raw_temp); +// Converts a temperture temp in °C into a raw value +// which can be compared with results of read_raw_temperature +extern int conv_temp_raw(byte type,float temp); +// Updates the temperature of all extruders and heated bed if it's time. +// Toggels the heater power if necessary. +extern void manage_temperatures(); +extern bool reportTempsensorError(); ///< Report defect sensors +extern byte manage_monitor; + +void process_command(GCode *code,byte bufferedCommand); + +void manage_inactivity(byte debug); + +extern void wait_until_end_of_move(); +extern void update_ramps_parameter(); +extern void update_extruder_flags(); +extern void finishNextSegment(); +extern void printPosition(); +extern void defaultLoopActions(); +extern void change_feedrate_multiply(int factor); ///< Set feedrate multiplier +extern void set_fan_speed(int speed,bool wait); /// Set fan speed 0..255 +extern void home_axis(bool xaxis,bool yaxis,bool zaxis); /// Home axis +extern byte get_coordinates(GCode *com); +extern void move_steps(long x,long y,long z,long e,float feedrate,bool waitEnd,bool check_endstop); +extern void queue_move(byte check_endstops,byte pathOptimize); +#if DRIVE_SYSTEM==3 +extern byte calculate_delta(long cartesianPosSteps[], long deltaPosSteps[]); +extern void set_delta_position(long xaxis, long yaxis, long zaxis); +extern float rodMaxLength; +extern void split_delta_move(byte check_endstops,byte pathOptimize, byte softEndstop); +#ifdef SOFTWARE_LEVELING +extern void calculate_plane(long factors[], long p1[], long p2[], long p3[]); +extern float calc_zoffset(long factors[], long pointX, long pointY); +#endif +#endif +extern void linear_move(long steps_remaining[]); +extern inline void disable_x(); +extern inline void disable_y(); +extern inline void disable_z(); +extern inline void enable_x(); +extern inline void enable_y(); +extern inline void enable_z(); + +#define PREVIOUS_PLANNER_INDEX(p) {p--;if(p==255) p = MOVE_CACHE_SIZE-1;} +#define NEXT_PLANNER_INDEX(idx) {++idx;if(idx==MOVE_CACHE_SIZE) idx=0;} + +extern void kill(byte only_steppers); + +extern float axis_steps_per_unit[]; +extern float inv_axis_steps_per_unit[]; +extern float max_feedrate[]; +extern float homing_feedrate[]; +extern float max_start_speed_units_per_second[]; +extern long max_acceleration_units_per_sq_second[]; +extern long max_travel_acceleration_units_per_sq_second[]; +extern unsigned long axis_steps_per_sqr_second[]; +extern unsigned long axis_travel_steps_per_sqr_second[]; +extern byte relative_mode; ///< Determines absolute (false) or relative Coordinates (true). +extern byte relative_mode_e; ///< Determines Absolute or Relative E Codes while in Absolute Coordinates mode. E is always relative in Relative Coordinates mode. + +extern byte unit_inches; +extern unsigned long previous_millis_cmd; +extern unsigned long max_inactive_time; +extern unsigned long stepper_inactive_time; + +extern void setupTimerInterrupt(); +extern void current_control_init(); +extern void microstep_init(); +extern void print_temperatures(); +extern void check_mem(); +#if ARC_SUPPORT +extern void mc_arc(float *position, float *target, float *offset, float radius, uint8_t isclockwise); +#endif + +#define PRINTER_FLAG0_STEPPER_DISABLED 1 +#define PRINTER_FLAG0_SEPERATE_EXTRUDER_INT 2 +#define PRINTER_FLAG0_TEMPSENSOR_DEFECT 4 +typedef struct { + byte flag0; // 1 = stepper disabled, 2 = use external extruder interrupt, 4 = temp Sensor defect +#if USE_OPS==1 || defined(USE_ADVANCE) + volatile int extruderStepsNeeded; ///< This many extruder steps are still needed, <0 = reverse steps needed. +// float extruderSpeed; ///< Extruder speed in mm/s. + byte minExtruderSpeed; ///< Timer delay for start extruder speed + byte maxExtruderSpeed; ///< Timer delay for end extruder speed + byte extruderAccelerateDelay; ///< delay between 2 speec increases +#endif + long interval; ///< Last step duration in ticks. +#if USE_OPS==1 + bool filamentRetracted; ///< Is the extruder filament retracted +#endif + unsigned long timer; ///< used for acceleration/deceleration timing + unsigned long stepNumber; ///< Step number in current move. +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + long advance_executed; ///< Executed advance steps +#endif + int advance_steps_set; + unsigned int advance_lin_set; +#endif + long currentPositionSteps[4]; ///< Position in steps from origin. + long destinationSteps[4]; ///< Target position in steps. +#if DRIVE_SYSTEM==3 +#ifdef STEP_COUNTER + long countZSteps; ///< Count of steps from last position reset +#endif + long currentDeltaPositionSteps[4]; + long maxDeltaPositionSteps; +#endif +#ifdef SOFTWARE_LEVELING + long levelingP1[3]; + long levelingP2[3]; + long levelingP3[3]; +#endif +#if USE_OPS==1 + int opsRetractSteps; ///< Retract filament this much steps + int opsPushbackSteps; ///< Retract+extra distance for backsash + float opsMinDistance; + float opsRetractDistance; + float opsRetractBacklash; + byte opsMode; ///< OPS operation mode. 0 = Off, 1 = Classic, 2 = Fast + float opsMoveAfter; ///< Start move after opsModeAfter percent off full retract. + int opsMoveAfterSteps; ///< opsMoveAfter converted in steps (negative value!). +#endif + long xMaxSteps; ///< For software endstops, limit of move in positive direction. + long yMaxSteps; ///< For software endstops, limit of move in positive direction. + long zMaxSteps; ///< For software endstops, limit of move in positive direction. + long xMinSteps; ///< For software endstops, limit of move in negative direction. + long yMinSteps; ///< For software endstops, limit of move in negative direction. + long zMinSteps; ///< For software endstops, limit of move in negative direction. + float xLength; + float xMin; + float yLength; + float yMin; + float zLength; + float zMin; + float feedrate; ///< Last requested feedrate. + int feedrateMultiply; ///< Multiplier for feedrate in percent (factor 1 = 100) + unsigned int extrudeMultiply; ///< Flow multiplier in percdent (factor 1 = 100) + float maxJerk; ///< Maximum allowed jerk in mm/s + float maxZJerk; ///< Maximum allowed jerk in z direction in mm/s + long offsetX; ///< X-offset for different extruder positions. + long offsetY; ///< Y-offset for different extruder positions. + unsigned int vMaxReached; ///< MAximumu reached speed + byte stepper_loops; + unsigned long msecondsPrinting; ///< Milliseconds of printing time (means time with heated extruder) + float filamentPrinted; ///< mm of filament printed since counting started + byte waslasthalfstepping; ///< Indicates if last move had halfstepping enabled +#if ENABLE_BACKLASH_COMPENSATION + float backlashX; + float backlashY; + float backlashZ; + byte backlashDir; +#endif +#ifdef DEBUG_STEPCOUNT + long totalStepsRemaining; +#endif +#if FEATURE_MEMORY_POSITION + long memoryX; + long memoryY; + long memoryZ; +#endif +#ifdef XY_GANTRY + char motorX; + char motorY; +#endif +} PrinterState; +extern PrinterState printer_state; + +/** Marks the first step of a new move */ +#define FLAG_WARMUP 1 +#define FLAG_NOMINAL 2 +#define FLAG_DECELERATING 4 +#define FLAG_ACCELERATION_ENABLED 8 +#define FLAG_CHECK_ENDSTOPS 16 +#define FLAG_SKIP_ACCELERATING 32 +#define FLAG_SKIP_DEACCELERATING 64 +#define FLAG_BLOCKED 128 + +/** Are the step parameter computed */ +#define FLAG_JOIN_STEPPARAMS_COMPUTED 1 +/** The right speed is fixed. Don't check this block or any block to the left. */ +#define FLAG_JOIN_END_FIXED 2 +/** The left speed is fixed. Don't check left block. */ +#define FLAG_JOIN_START_FIXED 4 +/** Start filament retraction at move start */ +#define FLAG_JOIN_START_RETRACT 8 +/** Wait for filament pushback, before ending move */ +#define FLAG_JOIN_END_RETRACT 16 +/** Disable retract for this line */ +#define FLAG_JOIN_NO_RETRACT 32 +/** Wait for the extruder to finish it's up movement */ +#define FLAG_JOIN_WAIT_EXTRUDER_UP 64 +/** Wait for the extruder to finish it's down movement */ +#define FLAG_JOIN_WAIT_EXTRUDER_DOWN 128 +// Printing related data +#if DRIVE_SYSTEM==3 +// Allow the delta cache to store segments for every line in line cache. Beware this gets big ... fast. +// MAX_DELTA_SEGMENTS_PER_LINE * +#define DELTA_CACHE_SIZE (MAX_DELTA_SEGMENTS_PER_LINE * MOVE_CACHE_SIZE) +typedef struct { + byte dir; ///< Direction of delta movement. + unsigned int deltaSteps[3]; ///< Number of steps in move. +} DeltaSegment; +extern DeltaSegment segments[]; // Delta segment cache +extern unsigned int delta_segment_write_pos; // Position where we write the next cached delta move +extern volatile unsigned int delta_segment_count; // Number of delta moves cached 0 = nothing in cache +extern byte lastMoveID; +#endif +typedef struct { // RAM usage: 24*4+15 = 113 Byte + byte primaryAxis; + volatile byte flags; + long timeInTicks; + byte joinFlags; + byte halfstep; ///< 0 = disabled, 1 = halfstep, 2 = fulstep + byte dir; ///< Direction of movement. 1 = X+, 2 = Y+, 4= Z+, values can be combined. + long delta[4]; ///< Steps we want to move. + long error[4]; ///< Error calculation for Bresenham algorithm + float speedX; ///< Speed in x direction at fullInterval in mm/s + float speedY; ///< Speed in y direction at fullInterval in mm/s + float speedZ; ///< Speed in z direction at fullInterval in mm/s + float speedE; ///< Speed in E direction at fullInterval in mm/s + float fullSpeed; ///< Desired speed mm/s + float invFullSpeed; ///< 1.0/fullSpeed for fatser computation + float acceleration; ///< Real 2.0*distanceÜacceleration mm²/s² + float maxJunctionSpeed; ///< Max. junction speed between this and next segment + float startSpeed; ///< Staring speed in mm/s + float endSpeed; ///< Exit speed in mm/s + float distance; +#if DRIVE_SYSTEM==3 + byte numDeltaSegments; ///< Number of delta segments left in line. Decremented by stepper timer. + byte moveID; ///< ID used to identify moves which are all part of the same line + int deltaSegmentReadPos; ///< Pointer to next DeltaSegment + long numPrimaryStepPerSegment; ///< Number of primary bresenham axis steps in each delta segment +#endif + unsigned long fullInterval; ///< interval at full speed in ticks/step. + unsigned long stepsRemaining; ///< Remaining steps, until move is finished + unsigned int accelSteps; ///< How much steps does it take, to reach the plateau. + unsigned int decelSteps; ///< How much steps does it take, to reach the end speed. + unsigned long accelerationPrim; ///< Acceleration along primary axis + unsigned long facceleration; ///< accelerationPrim*262144/F_CPU + unsigned int vMax; ///< Maximum reached speed in steps/s. + unsigned int vStart; ///< Starting speed in steps/s. + unsigned int vEnd; ///< End speed in steps/s +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + long advanceRate; ///< Advance steps at full speed + long advanceFull; ///< Maximum advance at fullInterval [steps*65536] + long advanceStart; + long advanceEnd; +#endif + unsigned int advanceL; ///< Recomputated L value +#endif +#if USE_OPS==1 + long opsReverseSteps; ///< How many steps are needed to reverse retracted filament at full speed +#endif +#ifdef DEBUG_STEPCOUNT + long totalStepsRemaining; +#endif +} PrintLine; + +extern PrintLine lines[]; +extern byte lines_write_pos; // Position where we write the next cached line move +extern byte lines_pos; // Position for executing line movement +extern volatile byte lines_count; // Number of lines cached 0 = nothing to do +extern byte printmoveSeen; +extern long baudrate; +#if OS_ANALOG_INPUTS>0 +// Get last result for pin x +extern volatile uint osAnalogInputValues[OS_ANALOG_INPUTS]; +#endif +#define BEGIN_INTERRUPT_PROTECTED {byte sreg=SREG;__asm volatile( "cli" ::: "memory" ); +#define END_INTERRUPT_PROTECTED SREG=sreg;} +#define ESCAPE_INTERRUPT_PROTECTED SREG=sreg; + +#define SECONDS_TO_TICKS(s) (unsigned long)(s*(float)F_CPU) +extern long CPUDivU2(unsigned int divisor); + +extern unsigned int counter_periodical; +extern volatile byte execute_periodical; +extern byte counter_250ms; +extern void write_monitor(); +extern void check_periodical(); +#define CELSIUS_EXTRA_BITS 3 +#define ANALOG_REDUCE_BITS 0 +#define ANALOG_REDUCE_FACTOR 1 + +#if HAVE_HEATED_BED +#define NUM_TEMPERATURE_LOOPS NUM_EXTRUDER+1 +extern TemperatureController heatedBedController; +#else +#define NUM_TEMPERATURE_LOOPS NUM_EXTRUDER +#endif +#define TEMP_INT_TO_FLOAT(temp) ((float)(temp)/(float)(1< -1) + WRITE(X_ENABLE_PIN,!X_ENABLE_ON); +#endif +} +/** \brief Disable stepper motor for y direction. */ +inline void disable_y() { +#if (Y_ENABLE_PIN > -1) + WRITE(Y_ENABLE_PIN,!Y_ENABLE_ON); +#endif +} +/** \brief Disable stepper motor for z direction. */ +inline void disable_z() { +#if (Z_ENABLE_PIN > -1) + WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); +#endif +} +/** \brief Enable stepper motor for x direction. */ +inline void enable_x() { +#if (X_ENABLE_PIN > -1) + WRITE(X_ENABLE_PIN, X_ENABLE_ON); +#endif +} +/** \brief Enable stepper motor for y direction. */ +inline void enable_y() { +#if (Y_ENABLE_PIN > -1) + WRITE(Y_ENABLE_PIN, Y_ENABLE_ON); +#endif +} +/** \brief Enable stepper motor for z direction. */ +inline void enable_z() { +#if (Z_ENABLE_PIN > -1) + WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); +#endif +} + + +#if DRIVE_SYSTEM==3 +#define SIN_60 0.8660254037844386 +#define COS_60 0.5 +#define DELTA_DIAGONAL_ROD_STEPS (AXIS_STEPS_PER_MM * DELTA_DIAGONAL_ROD) +#define DELTA_DIAGONAL_ROD_STEPS_SQUARED (DELTA_DIAGONAL_ROD_STEPS * DELTA_DIAGONAL_ROD_STEPS) +#define DELTA_ZERO_OFFSET_STEPS (AXIS_STEPS_PER_MM * DELTA_ZERO_OFFSET) +#define DELTA_RADIUS_STEPS (AXIS_STEPS_PER_MM * DELTA_RADIUS) + +#define DELTA_TOWER1_X_STEPS -SIN_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER1_Y_STEPS -COS_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER2_X_STEPS SIN_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER2_Y_STEPS -COS_60*DELTA_RADIUS_STEPS +#define DELTA_TOWER3_X_STEPS 0.0 +#define DELTA_TOWER3_Y_STEPS DELTA_RADIUS_STEPS + +#define NUM_AXIS 4 +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 +#define E_AXIS 3 + +#endif + +#define STR(s) #s +#define XSTR(s) STR(s) + +#endif diff --git a/Repetier/SDCard.cpp b/Repetier/SDCard.cpp new file mode 100644 index 0000000..6629b72 --- /dev/null +++ b/Repetier/SDCard.cpp @@ -0,0 +1,289 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. +*/ + +#include "Reptier.h" +#include "ui.h" + +#if SDSUPPORT + +#ifndef SD_ALLOW_LONG_NAMES +#define SD_ALLOW_LONG_NAMES false +#endif + +SDCard sd; + +SDCard::SDCard() { + sdmode = false; + sdactive = false; + savetosd = false; + //power to SD reader +#if SDPOWER > -1 + SET_OUTPUT(SDPOWER); + WRITE(SDPOWER,HIGH); +#endif +#if defined(SDCARDDETECT) && SDCARDDETECT>-1 + SET_INPUT(SDCARDDETECT); + WRITE(SDCARDDETECT,HIGH); +#endif +} +void SDCard::automount() { +#if defined(SDCARDDETECT) && SDCARDDETECT>-1 + if(READ(SDCARDDETECT) != SDCARDDETECTINVERTED) { + if(sdactive) { // Card removed + sdactive = false; + savetosd = false; + sdmode = false; + UI_STATUS(UI_TEXT_SD_REMOVED); + OUT_P_LN(UI_TEXT_SD_REMOVED); + } + } else { + if(!sdactive) { + UI_STATUS(UI_TEXT_SD_INSERTED); + OUT_P_LN(UI_TEXT_SD_INSERTED); + initsd(); + } + } +#endif +} +void SDCard::initsd() { + sdactive = false; + #if SDSS >- 1 + /*if(dir[0].isOpen()) + dir[0].close();*/ + if(!fat.begin(SDSS,SPI_FULL_SPEED)) { + OUT_P_LN("SD init fail"); + return; + } + fat.setStdOut(&out); + sdactive = true; + #endif +} + +void SDCard::write_command(GCode *code) { + unsigned int sum1=0,sum2=0; // for fletcher-16 checksum + byte buf[100]; + byte p=2; + file.writeError = false; + int params = 128 | (code->params & ~1); + *(int*)buf = params; + if(GCODE_IS_V2(code)) { // Read G,M as 16 bit value + *(int*)&buf[p] = code->params2;p+=2; + if(GCODE_HAS_STRING(code)) + buf[p++] = strlen(code->text); + if(code->params & 2) {*(int*)&buf[p] = code->M;p+=2;} + if(code->params & 4) {*(int*)&buf[p]= code->G;p+=2;} + } else { + if(code->params & 2) {buf[p++] = (byte)code->M;} + if(code->params & 4) {buf[p++] = (byte)code->G;} + } + if(code->params & 8) {*(float*)&buf[p] = code->X;p+=4;} + if(code->params & 16) {*(float*)&buf[p] = code->Y;p+=4;} + if(code->params & 32) {*(float*)&buf[p] = code->Z;p+=4;} + if(code->params & 64) {*(float*)&buf[p] = code->E;p+=4;} + if(code->params & 256) {*(float*)&buf[p] = code->F;p+=4;} + if(code->params & 512) {buf[p++] = code->T;} + if(code->params & 1024) {*(long int*)&buf[p] = code->S;p+=4;} + if(code->params & 2048) {*(long int*)&buf[p] = code->P;p+=4;} + if(code->params2 & 1) {*(float*)&buf[p] = code->I;p+=4;} + if(code->params2 & 2) {*(float*)&buf[p] = code->J;p+=4;} + if(GCODE_HAS_STRING(code)) { // read 16 byte into string + char *sp = code->text; + if(GCODE_IS_V2(code)) { + byte i = strlen(code->text); + for(;i;i--) buf[p++] = *sp++; + } else { + for(byte i=0;i<16;++i) buf[p++] = *sp++; + } + } + byte *ptr = buf; + byte len = p; + while (len) { + byte tlen = len > 21 ? 21 : len; + len -= tlen; + do { + sum1 += *ptr++; + if(sum1>=255) sum1-=255; + sum2 += sum1; + if(sum2>=255) sum2-=255; + } while (--tlen); + } + buf[p++] = sum1; + buf[p++] = sum2; + file.write(buf,p); + if (file.writeError){ + OUT_P_LN("error writing to file"); + } +} +char *SDCard::createFilename(char *buffer,const dir_t &p) +{ + char *pos = buffer,*src = (char*)p.name; + for (byte i = 0; i < 11; i++,src++) + { + if (*src == ' ') continue; + if (i == 8) + *pos++ = '.'; + *pos++ = *src; + } + *pos = 0; + return pos; +} +bool SDCard::showFilename(const uint8_t *name) { + if (*name == DIR_NAME_DELETED || *name == '.') return false; +#if !SD_ALLOW_LONG_NAMES + byte i=11; + while(i--) { + if(*name=='~') return false; + name++; + } +#endif + return true; +} +void SDCard::lsRecursive(SdBaseFile *parent,byte level) +{ + dir_t *p; + uint8_t cnt=0; + char *oldpathend = pathend; + char filename[13]; + parent->rewind(); + while ((p= parent->readDirCache())) + { + if (p->name[0] == DIR_NAME_FREE) break; + if (!showFilename(p->name)) continue; + if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; + if( DIR_IS_SUBDIR(p)) + { + if(level>=SD_MAX_FOLDER_DEPTH) continue; // can't go deeper + if(levelcurPosition()/32 - 1; + if(next.open(parent,dirname, O_READ)) + lsRecursive(&next,level+1); + parent->seekSet(32 * (index + 1)); + *oldpathend = 0; + } else { + createFilename(filename,*p); + if(level) { + out.print(fullName); + out.print('/'); + } + out.print(filename); +#ifdef SD_EXTENDED_DIR + out.write(' '); + out.print(p->fileSize); +#endif + out.println(); + } + } +} +void SDCard::ls() { + OUT_P_LN("Begin file list"); + *fullName = 0; + pathend = fullName; + fat.chdir(); + lsRecursive(fat.vwd(),0); + OUT_P_LN("End file list"); +} + +void SDCard::selectFile(char *filename) { + if(!sdactive) return; + sdmode = false; + file.close(); + fat.chdir(); + if (file.open(fat.vwd(),filename, O_READ)) { + OUT_P("File opened:"); + out.print(filename); + OUT_P_L_LN(" Size:",file.fileSize()); + sdpos = 0; + filesize = file.fileSize(); + OUT_P_LN("File selected"); + } else { + OUT_P_LN("file.open failed"); + } +} +void SDCard::printStatus() { + if(sdactive){ + OUT_P_L("SD printing byte ",sdpos); + OUT_P_L_LN("/",filesize); + } else { + OUT_P_LN("Not SD printing"); + } +} +void SDCard::startWrite(char *filename) { + if(!sdactive) return; + file.close(); + sdmode = false; + fat.chdir(); + if(!file.open(filename, O_CREAT | O_APPEND | O_WRITE | O_TRUNC)) { + OUT_P("open failed, File: "); + out.print(filename); + out.print('.'); + } else { + UI_STATUS(UI_TEXT_UPLOADING); + savetosd = true; + OUT_P("Writing to file: "); + out.println(filename); + } +} +void SDCard::finishWrite() { + if(!savetosd) return; // already closed or never opened + file.sync(); + file.close(); + savetosd = false; + OUT_P_LN("Done saving file."); + UI_CLEAR_STATUS; +} +void SDCard::deleteFile(char *filename) { + if(!sdactive) return; + sdmode = false; + file.close(); + if(fat.remove(filename)) { + OUT_P_LN("File deleted"); + } else { + if(fat.rmdir(filename)) + OUT_P_LN("File deleted"); + else + OUT_P_LN("Deletion failed"); + } +} +void SDCard::makeDirectory(char *filename) { + if(!sdactive) return; + sdmode = false; + file.close(); + if(fat.mkdir(filename)) { + OUT_P_LN("Directory created"); + } else { + OUT_P_LN("Creation failed"); + } +} +#endif + diff --git a/Repetier/SdFat.cpp b/Repetier/SdFat.cpp new file mode 100644 index 0000000..f1bfeeb --- /dev/null +++ b/Repetier/SdFat.cpp @@ -0,0 +1,3761 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ + #include "Reptier.h" +#if SDSUPPORT +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#define COMPAT_PRE1 +#endif +//#include +//------------------------------------------------------------------------------ +#if USE_SERIAL_FOR_STD_OUT || !defined(UDR0) +Print* SdFat::stdOut_ = &Serial; +#else // USE_SERIAL_FOR_STD_OUT +class DefaultSerial : public Print { + public: +#ifdef COMPAT_PRE1 + void write(uint8_t b); +#else + size_t write(uint8_t b); +#endif +}; +#ifdef COMPAT_PRE1 + void +#else + size_t +#endif +DefaultSerial::write(uint8_t b) { + while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} + UDR0 = b; +#ifndef COMPAT_PRE1 + return 1; +#endif +} +//------------------------------------------------------------------------------ +static DefaultSerial defaultStdOut; + +Print* SdFat::stdOut_ = &defaultStdOut; +#endif // USE_SERIAL_FOR_STD_OUT +//------------------------------------------------------------------------------ +static void pstrPrint(PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) SdFat::stdOut()->write(c); +} +//------------------------------------------------------------------------------ +static void pstrPrintln(PGM_P str) { + pstrPrint(str); + SdFat::stdOut()->println(); +} +//------------------------------------------------------------------------------ +/** Change a volume's working directory to root + * + * Changes the volume's working directory to the SD's root directory. + * Optionally set the current working directory to the volume's + * working directory. + * + * \param[in] set_cwd Set the current working directory to this volume's + * working directory if true. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::chdir(bool set_cwd) { + if (set_cwd) SdBaseFile::cwd_ = &vwd_; + if (vwd_.isOpen()) vwd_.close(); + return vwd_.openRoot(&vol_); +} +//------------------------------------------------------------------------------ +/** Change a volume's working directory + * + * Changes the volume working directory to the \a path subdirectory. + * Optionally set the current working directory to the volume's + * working directory. + * + * Example: If the volume's working directory is "/DIR", chdir("SUB") + * will change the volume's working directory from "/DIR" to "/DIR/SUB". + * + * If path is "/", the volume's working directory will be changed to the + * root directory + * + * \param[in] path The name of the subdirectory. + * + * \param[in] set_cwd Set the current working directory to this volume's + * working directory if true. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::chdir(const char *path, bool set_cwd) { + SdBaseFile dir; + if (path[0] == '/' && path[1] == '\0') return chdir(set_cwd); + if (!dir.open(&vwd_, path, O_READ)) goto fail; + if (!dir.isDir()) goto fail; + vwd_ = dir; + if (set_cwd) SdBaseFile::cwd_ = &vwd_; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Set the current working directory to a volume's working directory. + * + * This is useful with multiple SD cards. + * + * The current working directory is changed to this volume's working directory. + * + * This is like the Windows/DOS \: command. + */ +void SdFat::chvol() { + SdBaseFile::cwd_ = &vwd_; +} +//------------------------------------------------------------------------------ +/** %Print any SD error code and halt. */ +void SdFat::errorHalt() { + errorPrint(); + while (1); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code, and halt. + * + * \param[in] msg Message to print. + */ +void SdFat::errorHalt(char const* msg) { + errorPrint(msg); + while (1); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code, and halt. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::errorHalt_P(PGM_P msg) { + errorPrint_P(msg); + while (1); +} +//------------------------------------------------------------------------------ +/** %Print any SD error code. */ +void SdFat::errorPrint() { + if (!card_.errorCode()) return; + pstrPrint(PSTR("SD errorCode: 0X")); + stdOut_->println(card_.errorCode(), HEX); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code. + * + * \param[in] msg Message to print. + */ +void SdFat::errorPrint(char const* msg) { + pstrPrint(PSTR("error: ")); + stdOut_->println(msg); + errorPrint(); +} +//------------------------------------------------------------------------------ +/** %Print msg, any SD error code. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::errorPrint_P(PGM_P msg) { + pstrPrint(PSTR("error: ")); + pstrPrintln(msg); + errorPrint(); +} +//------------------------------------------------------------------------------ +/** + * Test for the existence of a file. + * + * \param[in] name Name of the file to be tested for. + * + * \return true if the file exists else false. + */ +bool SdFat::exists(const char* name) { + return vwd_.exists(name); +} +//------------------------------------------------------------------------------ +/** + * Initialize an SdFat object. + * + * Initializes the SD card, SD volume, and root directory. + * + * \param[in] sckRateID value for SPI SCK rate. See Sd2Card::init(). + * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init(). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::init(uint8_t sckRateID, uint8_t chipSelectPin) { + return card_.init(sckRateID, chipSelectPin) && vol_.init(&card_) && chdir(1); +} +//------------------------------------------------------------------------------ +/** %Print error details and halt after SdFat::init() fails. */ +void SdFat::initErrorHalt() { + initErrorPrint(); + while (1); +} +//------------------------------------------------------------------------------ +/**Print message, error details, and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ +void SdFat::initErrorHalt(char const *msg) { + stdOut_->println(msg); + initErrorHalt(); +} +//------------------------------------------------------------------------------ +/**Print message, error details, and halt after SdFat::init() fails. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::initErrorHalt_P(PGM_P msg) { + pstrPrintln(msg); + initErrorHalt(); +} +//------------------------------------------------------------------------------ +/** Print error details after SdFat::init() fails. */ +void SdFat::initErrorPrint() { + if (card_.errorCode()) { + pstrPrintln(PSTR("Can't access SD card. Do not reformat.")); + if (card_.errorCode() == SD_CARD_ERROR_CMD0) { + pstrPrintln(PSTR("No card, wrong chip select pin, or SPI problem?")); + } + errorPrint(); + } else if (vol_.fatType() == 0) { + pstrPrintln(PSTR("Invalid format, reformat SD.")); + } else if (!vwd_.isOpen()) { + pstrPrintln(PSTR("Can't open root directory.")); + } else { + pstrPrintln(PSTR("No error found.")); + } +} +//------------------------------------------------------------------------------ +/**Print message and error details and halt after SdFat::init() fails. + * + * \param[in] msg Message to print. + */ +void SdFat::initErrorPrint(char const *msg) { + stdOut_->println(msg); + initErrorPrint(); +} +//------------------------------------------------------------------------------ +/**Print message and error details after SdFat::init() fails. + * + * \param[in] msg Message in program space (flash memory) to print. + */ +void SdFat::initErrorPrint_P(PGM_P msg) { + pstrPrintln(msg); + initErrorHalt(); +} +//------------------------------------------------------------------------------ +/** List the directory contents of the volume working directory to stdOut. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ +void SdFat::ls(uint8_t flags) { + vwd_.ls(stdOut_, flags); +} +//------------------------------------------------------------------------------ +/** List the directory contents of the volume working directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ +void SdFat::ls(Print* pr, uint8_t flags) { + vwd_.ls(pr, flags); +} +//------------------------------------------------------------------------------ +/** Make a subdirectory in the volume working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::mkdir(const char* path, bool pFlag) { + SdBaseFile sub; + return sub.mkdir(&vwd_, path, pFlag); +} +//------------------------------------------------------------------------------ +/** Remove a file from the volume working directory. +* +* \param[in] path A path with a valid 8.3 DOS name for the file. +* +* \return The value one, true, is returned for success and +* the value zero, false, is returned for failure. +*/ +bool SdFat::remove(const char* path) { + return SdBaseFile::remove(&vwd_, path); +} +//------------------------------------------------------------------------------ +/** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::rename(const char *oldPath, const char *newPath) { + SdBaseFile file; + if (!file.open(oldPath, O_READ)) return false; + return file.rename(&vwd_, newPath); +} +//------------------------------------------------------------------------------ +/** Remove a subdirectory from the volume's working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdFat::rmdir(const char* path) { + SdBaseFile sub; + if (!sub.open(path, O_READ)) return false; + return sub.rmdir(); +} +//------------------------------------------------------------------------------ +/** Truncate a file to a specified length. The current file position + * will be maintained if it is less than or equal to \a length otherwise + * it will be set to end of file. + * + * \param[in] path A path with a valid 8.3 DOS name for the file. + * \param[in] length The desired length for the file. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include file is read only, file is a directory, + * \a length is greater than the current file size or an I/O error occurs. + */ +bool SdFat::truncate(const char* path, uint32_t length) { + SdBaseFile file; + if (!file.open(path, O_WRITE)) return false; + return file.truncate(length); +} + +// macro for debug +#define DBG_FAIL_MACRO // Serial.println(__LINE__) +//------------------------------------------------------------------------------ +// pointer to cwd directory +SdBaseFile* SdBaseFile::cwd_ = 0; +// callback function for date/time +void (*SdBaseFile::dateTime_)(uint16_t* date, uint16_t* time) = 0; +//------------------------------------------------------------------------------ +// add a cluster to a file +bool SdBaseFile::addCluster() { + if (!vol_->allocContiguous(1, &curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + // if first cluster of file link to directory entry + if (firstCluster_ == 0) { + firstCluster_ = curCluster_; + flags_ |= F_FILE_DIR_DIRTY; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// Add a cluster to a directory file and zero the cluster. +// return with first block of cluster in the cache +bool SdBaseFile::addDirCluster() { + uint32_t block; + // max folder size + if (fileSize_/sizeof(dir_t) >= 0XFFFF) { + DBG_FAIL_MACRO; + goto fail; + } + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!vol_->cacheFlush()) { + DBG_FAIL_MACRO; + goto fail; + } + block = vol_->clusterStartBlock(curCluster_); + + // set cache to first block of cluster + vol_->cacheSetBlockNumber(block, true); + + // zero first block of cluster + memset(vol_->cacheBuffer_.data, 0, 512); + + // zero rest of cluster + for (uint8_t i = 1; i < vol_->blocksPerCluster_; i++) { + if (!vol_->writeBlock(block + i, vol_->cacheBuffer_.data)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // Increase directory file size by cluster size + fileSize_ += 512UL << vol_->clusterSizeShift_; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// cache a file's directory entry +// return pointer to cached entry or null for failure +dir_t* SdBaseFile::cacheDirEntry(uint8_t action) { + if (!vol_->cacheRawBlock(dirBlock_, action)) { + DBG_FAIL_MACRO; + goto fail; + } + return vol_->cache()->dir + dirIndex_; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +/** Close a file and force cached data and directory information + * to be written to the storage device. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include no file is open or an I/O error. + */ +bool SdBaseFile::close() { + bool rtn = sync(); + type_ = FAT_FILE_TYPE_CLOSED; + return rtn; +} +//------------------------------------------------------------------------------ +/** Check for contiguous file and return its raw block range. + * + * \param[out] bgnBlock the first block address for the file. + * \param[out] endBlock the last block address for the file. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include file is not contiguous, file has zero length + * or an I/O error occurred. + */ +bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { + // error if no blocks + if (firstCluster_ == 0) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint32_t c = firstCluster_; ; c++) { + uint32_t next; + if (!vol_->fatGet(c, &next)) { + DBG_FAIL_MACRO; + goto fail; + } + // check for contiguous + if (next != (c + 1)) { + // error if not end of chain + if (!vol_->isEOC(next)) { + DBG_FAIL_MACRO; + goto fail; + } + *bgnBlock = vol_->clusterStartBlock(firstCluster_); + *endBlock = vol_->clusterStartBlock(c) + + vol_->blocksPerCluster_ - 1; + return true; + } + } + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Create and open a new contiguous file of a specified size. + * + * \note This function only supports short DOS 8.3 names. + * See open() for more information. + * + * \param[in] dirFile The directory where the file will be created. + * \param[in] path A path with a valid DOS 8.3 file name. + * \param[in] size The desired file size. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include \a path contains + * an invalid DOS 8.3 file name, the FAT volume has not been initialized, + * a file is already open, the file already exists, the root + * directory is full or an I/O error. + * + */ +bool SdBaseFile::createContiguous(SdBaseFile* dirFile, + const char* path, uint32_t size) { + uint32_t count; + // don't allow zero length file + if (size == 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) { + DBG_FAIL_MACRO; + goto fail; + } + // calculate number of clusters needed + count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; + + // allocate clusters + if (!vol_->allocContiguous(count, &firstCluster_)) { + remove(); + DBG_FAIL_MACRO; + goto fail; + } + fileSize_ = size; + + // insure sync() will update dir entry + flags_ |= F_FILE_DIR_DIRTY; + + return sync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Return a file's directory entry. + * + * \param[out] dir Location for return of the file's directory entry. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::dirEntry(dir_t* dir) { + dir_t* p; + // make sure fields on SD are correct + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // read entry + p = cacheDirEntry(SdVolume::CACHE_FOR_READ); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // copy to caller's struct + memcpy(dir, p, sizeof(dir_t)); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Format the name field of \a dir into the 13 byte array + * \a name in standard 8.3 short name format. + * + * \param[in] dir The directory structure containing the name. + * \param[out] name A 13 byte char array for the formatted name. + */ +void SdBaseFile::dirName(const dir_t& dir, char* name) { + uint8_t j = 0; + for (uint8_t i = 0; i < 11; i++) { + if (dir.name[i] == ' ')continue; + if (i == 8) name[j++] = '.'; + name[j++] = dir.name[i]; + } + name[j] = 0; +} +//------------------------------------------------------------------------------ +/** Test for the existence of a file in a directory + * + * \param[in] name Name of the file to be tested for. + * + * The calling instance must be an open directory file. + * + * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory + * dirFile. + * + * \return true if the file exists else false. + */ +bool SdBaseFile::exists(const char* name) { + SdBaseFile file; + return file.open(this, name, O_READ); +} +//------------------------------------------------------------------------------ +/** + * Get a string from a file. + * + * fgets() reads bytes from a file into the array pointed to by \a str, until + * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str, + * or end-of-file is encountered. The string is then terminated + * with a null byte. + * + * fgets() deletes CR, '\\r', from the string. This insures only a '\\n' + * terminates the string for Windows text files which use CRLF for newline. + * + * \param[out] str Pointer to the array where the string is stored. + * \param[in] num Maximum number of characters to be read + * (including the final null byte). Usually the length + * of the array \a str is used. + * \param[in] delim Optional set of delimiters. The default is "\n". + * + * \return For success fgets() returns the length of the string in \a str. + * If no data is read, fgets() returns zero for EOF or -1 if an error occurred. + **/ +int16_t SdBaseFile::fgets(char* str, int16_t num, char* delim) { + char ch; + int16_t n = 0; + int16_t r = -1; + while ((n + 1) < num && (r = read(&ch, 1)) == 1) { + // delete CR + if (ch == '\r') continue; + str[n++] = ch; + if (!delim) { + if (ch == '\n') break; + } else { + if (strchr(delim, ch)) break; + } + } + if (r < 0) { + // read error + return -1; + } + str[n] = '\0'; + return n; +} +//------------------------------------------------------------------------------ +/** Get a file's name + * + * \param[out] name An array of 13 characters for the file's name. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::getFilename(char* name) { + dir_t* p; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isRoot()) { + name[0] = '/'; + name[1] = '\0'; + return true; + } + // cache entry + p = cacheDirEntry(SdVolume::CACHE_FOR_READ); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // format name + dirName(*p, name); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +void SdBaseFile::getpos(fpos_t* pos) { + pos->position = curPosition_; + pos->cluster = curCluster_; +} +//------------------------------------------------------------------------------ +/** List directory contents to stdOut. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ +void SdBaseFile::ls(uint8_t flags) { + ls(SdFat::stdOut(), flags, 0); +} +//------------------------------------------------------------------------------ +/** List directory contents. + * + * \param[in] pr Print stream for list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \param[in] indent Amount of space before file name. Used for recursive + * list to indicate subdirectory level. + */ +void SdBaseFile::ls(Print* pr, uint8_t flags, uint8_t indent) { + rewind(); + int8_t status; + while ((status = lsPrintNext(pr, flags, indent))) { + if (status > 1 && (flags & LS_R)) { + uint16_t index = curPosition()/32 - 1; + SdBaseFile s; + if (s.open(this, index, O_READ)) s.ls(pr, flags, indent + 2); + seekSet(32 * (index + 1)); + } + } +} +//------------------------------------------------------------------------------ +// saves 32 bytes on stack for ls recursion +// return 0 - EOF, 1 - normal file, or 2 - directory +int8_t SdBaseFile::lsPrintNext(Print *pr, uint8_t flags, uint8_t indent) { + dir_t dir; + uint8_t w = 0; + + while (1) { + if (read(&dir, sizeof(dir)) != sizeof(dir)) return 0; + if (dir.name[0] == DIR_NAME_FREE) return 0; + + // skip deleted entry and entries for . and .. + if (dir.name[0] != DIR_NAME_DELETED && dir.name[0] != '.' + && DIR_IS_FILE_OR_SUBDIR(&dir)) break; + } + // indent for dir level + for (uint8_t i = 0; i < indent; i++) pr->write(' '); + + // print name + for (uint8_t i = 0; i < 11; i++) { + if (dir.name[i] == ' ')continue; + if (i == 8) { + pr->write('.'); + w++; + } + pr->write(dir.name[i]); + w++; + } + if (DIR_IS_SUBDIR(&dir)) { + pr->write('/'); + w++; + } + if (flags & (LS_DATE | LS_SIZE)) { + while (w++ < 14) pr->write(' '); + } + // print modify date/time if requested + if (flags & LS_DATE) { + pr->write(' '); + printFatDate(pr, dir.lastWriteDate); + pr->write(' '); + printFatTime(pr, dir.lastWriteTime); + } + // print size if requested + if (!DIR_IS_SUBDIR(&dir) && (flags & LS_SIZE)) { + pr->write(' '); + pr->print(dir.fileSize); + } + pr->println(); + return DIR_IS_FILE(&dir) ? 1 : 2; +} +//------------------------------------------------------------------------------ +// format directory name field from a 8.3 name string +bool SdBaseFile::make83Name(const char* str, uint8_t* name, const char** ptr) { + uint8_t c; + uint8_t n = 7; // max index for part before dot + uint8_t i = 0; + // blank fill name and extension + while (i < 11) name[i++] = ' '; + i = 0; + while (*str != '\0' && *str != '/') { + c = *str++; + if (c == '.') { + if (n == 10) { + DBG_FAIL_MACRO; + goto fail; // only one dot allowed + } + n = 10; // max index for full 8.3 name + i = 8; // place for extension + } else { + // illegal FAT characters +#define FLASH_ILLEGAL_CHARS +#ifdef FLASH_ILLEGAL_CHARS + // store chars in flash + PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); + uint8_t b; + while ((b = pgm_read_byte(p++))) if (b == c) { + DBG_FAIL_MACRO; + goto fail; + } +#else // FLASH_ILLEGAL_CHARS + // store chars in RAM + if (strchr("|<>^+=?/[];,*\"\\", c)) { + DBG_FAIL_MACRO; + goto fail; + } +#endif // FLASH_ILLEGAL_CHARS + + // check size and only allow ASCII printable characters + if (i > n || c < 0X21 || c > 0X7E) { + DBG_FAIL_MACRO; + goto fail; + } + // only upper case allowed in 8.3 names - convert lower to upper + name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); + } + } + *ptr = str; + // must have a file name, extension is optional + return name[0] != ' '; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Make a new directory. + * + * \param[in] parent An open SdFat instance for the directory that will contain + * the new directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the new directory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include this file is already open, \a parent is not a + * directory, \a path is invalid or already exists in \a parent. + */ +bool SdBaseFile::mkdir(SdBaseFile* parent, const char* path, bool pFlag) { + uint8_t dname[11]; + SdBaseFile dir1, dir2; + SdBaseFile* sub = &dir1; + SdBaseFile* start = parent; + + if (!parent || isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (*path == '/') { + while (*path == '/') path++; + if (!parent->isRoot()) { + if (!dir2.openRoot(parent->vol_)) { + DBG_FAIL_MACRO; + goto fail; + } + parent = &dir2; + } + } + while (1) { + if (!make83Name(path, dname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + while (*path == '/') path++; + if (!*path) break; + if (!sub->open(parent, dname, O_READ)) { + if (!pFlag || !sub->mkdir(parent, dname)) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (parent != start) parent->close(); + parent = sub; + sub = parent != &dir1 ? &dir1 : &dir2; + } + return mkdir(parent, dname); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { + uint32_t block; + dir_t d; + dir_t* p; + + if (!parent->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // create a normal file + if (!open(parent, dname, O_CREAT | O_EXCL | O_RDWR)) { + DBG_FAIL_MACRO; + goto fail; + } + // convert file to directory + flags_ = O_READ; + type_ = FAT_FILE_TYPE_SUBDIR; + + // allocate and zero first cluster + if (!addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + // force entry to SD + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // cache entry - should already be in cache due to sync() call + p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // change directory entry attribute + p->attributes = DIR_ATT_DIRECTORY; + + // make entry for '.' + memcpy(&d, p, sizeof(d)); + d.name[0] = '.'; + for (uint8_t i = 1; i < 11; i++) d.name[i] = ' '; + + // cache block for '.' and '..' + block = vol_->clusterStartBlock(firstCluster_); + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // copy '.' to block + memcpy(&vol_->cache()->dir[0], &d, sizeof(d)); + + // make entry for '..' + d.name[1] = '.'; + if (parent->isRoot()) { + d.firstClusterLow = 0; + d.firstClusterHigh = 0; + } else { + d.firstClusterLow = parent->firstCluster_ & 0XFFFF; + d.firstClusterHigh = parent->firstCluster_ >> 16; + } + // copy '..' to block + memcpy(&vol_->cache()->dir[1], &d, sizeof(d)); + + // write first block + return vol_->cacheFlush(); + + fail: + return false; +} +//------------------------------------------------------------------------------ + /** Open a file in the current working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ + bool SdBaseFile::open(const char* path, uint8_t oflag) { + return open(cwd_, path, oflag); + } +//------------------------------------------------------------------------------ +/** Open a file or directory by name. + * + * \param[in] dirFile An open SdFat instance for the directory containing the + * file to be opened. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags from the following list + * + * O_READ - Open for reading. + * + * O_RDONLY - Same as O_READ. + * + * O_WRITE - Open for writing. + * + * O_WRONLY - Same as O_WRITE. + * + * O_RDWR - Open for reading and writing. + * + * O_APPEND - If set, the file offset shall be set to the end of the + * file prior to each write. + * + * O_AT_END - Set the initial position at the end of the file. + * + * O_CREAT - If the file exists, this flag has no effect except as noted + * under O_EXCL below. Otherwise, the file shall be created + * + * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. + * + * O_SYNC - Call sync() after each write. This flag should not be used with + * write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. + * These functions do character at a time writes so sync() will be called + * after each byte. + * + * O_TRUNC - If the file exists and is a regular file, and the file is + * successfully opened and is not read only, its length shall be truncated to 0. + * + * WARNING: A given file must not be opened by more than one SdBaseFile object + * of file corruption may occur. + * + * \note Directory files must be opened read only. Write and truncation is + * not allowed for directory files. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include this file is already open, \a dirFile is not + * a directory, \a path is invalid, the file does not exist + * or can't be opened in the access mode specified by oflag. + */ +bool SdBaseFile::open(SdBaseFile* dirFile, const char* path, uint8_t oflag) { + uint8_t dname[11]; + SdBaseFile dir1, dir2; + SdBaseFile *parent = dirFile; + SdBaseFile *sub = &dir1; + + if (!dirFile) { + DBG_FAIL_MACRO; + goto fail; + } + // error if already open + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (*path == '/') { + while (*path == '/') path++; + if (!dirFile->isRoot()) { + if (!dir2.openRoot(dirFile->vol_)) { + DBG_FAIL_MACRO; + goto fail; + } + parent = &dir2; + } + } + while (1) { + if (!make83Name(path, dname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + while (*path == '/') path++; + if (!*path) break; + if (!sub->open(parent, dname, O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + if (parent != dirFile) parent->close(); + parent = sub; + sub = parent != &dir1 ? &dir1 : &dir2; + } + return open(parent, dname, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +// open with filename in dname +bool SdBaseFile::open(SdBaseFile* dirFile, + const uint8_t dname[11], uint8_t oflag) { + bool emptyFound = false; + bool fileFound = false; + uint8_t index; + dir_t* p; + + vol_ = dirFile->vol_; + + dirFile->rewind(); + // search for file + + while (dirFile->curPosition_ < dirFile->fileSize_) { + index = 0XF & (dirFile->curPosition_ >> 5); + p = dirFile->readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { + // remember first empty slot + if (!emptyFound) { + dirBlock_ = dirFile->vol_->cacheBlockNumber(); + dirIndex_ = index; + emptyFound = true; + } + // done if no entries follow + if (p->name[0] == DIR_NAME_FREE) break; + } else if (!memcmp(dname, p->name, 11)) { + fileFound = true; + break; + } + } + if (fileFound) { + // don't open existing file if O_EXCL + if (oflag & O_EXCL) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // don't create unless O_CREAT and O_WRITE + if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + if (emptyFound) { + index = dirIndex_; + p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) { + DBG_FAIL_MACRO; + goto fail; + } + // add and zero cluster for dirFile - first cluster is in cache for write + if (!dirFile->addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + // use first entry in cluster + p = dirFile->vol_->cache()->dir; + index = 0; + } + // initialize as empty file + memset(p, 0, sizeof(dir_t)); + memcpy(p->name, dname, 11); + + // set timestamps + if (dateTime_) { + // call user date/time function + dateTime_(&p->creationDate, &p->creationTime); + } else { + // use default date/time + p->creationDate = FAT_DEFAULT_DATE; + p->creationTime = FAT_DEFAULT_TIME; + } + p->lastAccessDate = p->creationDate; + p->lastWriteDate = p->creationDate; + p->lastWriteTime = p->creationTime; + + // write entry to SD + if (!dirFile->vol_->cacheFlush()) { + DBG_FAIL_MACRO; + goto fail; + } + } + // open entry in cache + return openCachedEntry(index, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Open a file by index. + * + * \param[in] dirFile An open SdFat instance for the directory. + * + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ +bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) { + dir_t* p; + + vol_ = dirFile->vol_; + + // error if already open + if (isOpen() || !dirFile) { + DBG_FAIL_MACRO; + goto fail; + } + + // don't open existing file if O_EXCL - user call error + if (oflag & O_EXCL) { + DBG_FAIL_MACRO; + goto fail; + } + // seek to location of entry + if (!dirFile->seekSet(32 * index)) { + DBG_FAIL_MACRO; + goto fail; + } + // read entry into cache + p = dirFile->readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // error if empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_FREE || + p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + DBG_FAIL_MACRO; + goto fail; + } + // open cached entry + return openCachedEntry(index & 0XF, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +// open a cached directory entry. Assumes vol_ is initialized +bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { + // location of entry in cache + dir_t* p = &vol_->cache()->dir[dirIndex]; + + // write or truncate is an error for a directory or read-only file + if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) { + if (oflag & (O_WRITE | O_TRUNC)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // remember location of directory entry on SD + dirBlock_ = vol_->cacheBlockNumber(); + dirIndex_ = dirIndex; + + // copy first cluster number for directory fields + firstCluster_ = (uint32_t)p->firstClusterHigh << 16; + firstCluster_ |= p->firstClusterLow; + + // make sure it is a normal file or subdirectory + if (DIR_IS_FILE(p)) { + fileSize_ = p->fileSize; + type_ = FAT_FILE_TYPE_NORMAL; + } else if (DIR_IS_SUBDIR(p)) { + if (!vol_->chainSize(firstCluster_, &fileSize_)) { + DBG_FAIL_MACRO; + goto fail; + } + type_ = FAT_FILE_TYPE_SUBDIR; + } else { + DBG_FAIL_MACRO; + goto fail; + } + // save open flags for read/write + flags_ = oflag & F_OFLAG; + + // set to start of file + curCluster_ = 0; + curPosition_ = 0; + if ((oflag & O_TRUNC) && !truncate(0)) { + DBG_FAIL_MACRO; + goto fail; + } + return oflag & O_AT_END ? seekEnd(0) : true; + + fail: + type_ = FAT_FILE_TYPE_CLOSED; + return false; +} +//------------------------------------------------------------------------------ +/** Open the next file or subdirectory in a directory. + * + * \param[in] dirFile An open SdFat instance for the directory containing the + * file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ +bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { + dir_t* p; + uint8_t index; + + if (!dirFile) { + DBG_FAIL_MACRO; + goto fail; + } + // error if already open + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + vol_ = dirFile->vol_; + + while (1) { + index = 0XF & (dirFile->curPosition_ >> 5); + + // read entry into cache + p = dirFile->readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // done if last entry + if (p->name[0] == DIR_NAME_FREE) { + DBG_FAIL_MACRO; + goto fail; + } + // skip empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + continue; + } + // must be file or dir + if (DIR_IS_FILE_OR_SUBDIR(p)) { + return openCachedEntry(index, oflag); + } + } + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Open a directory's parent directory. + * + * \param[in] dir Parent of this directory will be opened. Must not be root. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::openParent(SdBaseFile* dir) { + dir_t entry; + dir_t* p; + SdBaseFile file; + uint32_t c; + uint32_t cluster; + uint32_t lbn; + // error if already open or dir is root or dir is not a directory + if (isOpen() || !dir || dir->isRoot() || !dir->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + vol_ = dir->vol_; + // position to '..' + if (!dir->seekSet(32)) { + DBG_FAIL_MACRO; + goto fail; + } + // read '..' entry + if (dir->read(&entry, sizeof(entry)) != 32) { + DBG_FAIL_MACRO; + goto fail; + } + // verify it is '..' + if (entry.name[0] != '.' || entry.name[1] != '.') { + DBG_FAIL_MACRO; + goto fail; + } + // start cluster for '..' + cluster = entry.firstClusterLow; + cluster |= (uint32_t)entry.firstClusterHigh << 16; + if (cluster == 0) return openRoot(vol_); + // start block for '..' + lbn = vol_->clusterStartBlock(cluster); + // first block of parent dir + if (!vol_->cacheRawBlock(lbn, SdVolume::CACHE_FOR_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + p = &vol_->cacheBuffer_.dir[1]; + // verify name for '../..' + if (p->name[0] != '.' || p->name[1] != '.') { + DBG_FAIL_MACRO; + goto fail; + } + // '..' is pointer to first cluster of parent. open '../..' to find parent + if (p->firstClusterHigh == 0 && p->firstClusterLow == 0) { + if (!file.openRoot(dir->volume())) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + if (!file.openCachedEntry(1, O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // search for parent in '../..' + do { + if (file.readDir(&entry) != 32) { + DBG_FAIL_MACRO; + goto fail; + } + c = entry.firstClusterLow; + c |= (uint32_t)entry.firstClusterHigh << 16; + } while (c != cluster); + // open parent + return open(&file, file.curPosition()/32 - 1, O_READ); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Open a volume's root directory. + * + * \param[in] vol The FAT volume containing the root directory to be opened. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file is already open, the FAT volume has + * not been initialized or it a FAT12 volume. + */ +bool SdBaseFile::openRoot(SdVolume* vol) { + // error if file is already open + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (vol->fatType() == 16 || (FAT12_SUPPORT && vol->fatType() == 12)) { + type_ = FAT_FILE_TYPE_ROOT_FIXED; + firstCluster_ = 0; + fileSize_ = 32 * vol->rootDirEntryCount(); + } else if (vol->fatType() == 32) { + type_ = FAT_FILE_TYPE_ROOT32; + firstCluster_ = vol->rootDirStart(); + if (!vol->chainSize(firstCluster_, &fileSize_)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // volume is not initialized, invalid, or FAT12 without support + DBG_FAIL_MACRO; + goto fail; + } + vol_ = vol; + // read only + flags_ = O_READ; + + // set to start of file + curCluster_ = 0; + curPosition_ = 0; + + // root has no directory entry + dirBlock_ = 0; + dirIndex_ = 0; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ +int SdBaseFile::peek() { + fpos_t pos; + getpos(&pos); + int c = read(); + if (c >= 0) setpos(&pos); + return c; +} +//------------------------------------------------------------------------------ +/** %Print the name field of a directory entry in 8.3 format to stdOut. + * + * \param[in] dir The directory structure containing the name. + * \param[in] width Blank fill name if length is less than \a width. + * \param[in] printSlash Print '/' after directory names if true. + */ +void SdBaseFile::printDirName(const dir_t& dir, + uint8_t width, bool printSlash) { + printDirName(SdFat::stdOut(), dir, width, printSlash); +} +//------------------------------------------------------------------------------ +/** %Print the name field of a directory entry in 8.3 format. + * \param[in] pr Print stream for output. + * \param[in] dir The directory structure containing the name. + * \param[in] width Blank fill name if length is less than \a width. + * \param[in] printSlash Print '/' after directory names if true. + */ +void SdBaseFile::printDirName(Print* pr, const dir_t& dir, + uint8_t width, bool printSlash) { + uint8_t w = 0; + for (uint8_t i = 0; i < 11; i++) { + if (dir.name[i] == ' ')continue; + if (i == 8) { + pr->write('.'); + w++; + } + pr->write(dir.name[i]); + w++; + } + if (DIR_IS_SUBDIR(&dir) && printSlash) { + pr->write('/'); + w++; + } + while (w < width) { + pr->write(' '); + w++; + } +} +//------------------------------------------------------------------------------ +// print uint8_t with width 2 +static void print2u(Print* pr, uint8_t v) { + if (v < 10) pr->write('0'); + pr->print(v, DEC); +} +//------------------------------------------------------------------------------ +/** Print a file's creation date and time + * + * \param[in] pr Print stream for output. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printCreateDateTime(Print* pr) { + dir_t dir; + if (!dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + printFatDate(pr, dir.creationDate); + pr->write(' '); + printFatTime(pr, dir.creationTime); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** %Print a directory date field to stdOut. + * + * Format is yyyy-mm-dd. + * + * \param[in] fatDate The date field from a directory entry. + */ +void SdBaseFile::printFatDate(uint16_t fatDate) { + printFatDate(SdFat::stdOut(), fatDate); +} +//------------------------------------------------------------------------------ +/** %Print a directory date field. + * + * Format is yyyy-mm-dd. + * + * \param[in] pr Print stream for output. + * \param[in] fatDate The date field from a directory entry. + */ +void SdBaseFile::printFatDate(Print* pr, uint16_t fatDate) { + pr->print(FAT_YEAR(fatDate)); + pr->write('-'); + print2u(pr, FAT_MONTH(fatDate)); + pr->write('-'); + print2u(pr, FAT_DAY(fatDate)); +} +//------------------------------------------------------------------------------ +/** %Print a directory time field to stdOut. + * + * Format is hh:mm:ss. + * + * \param[in] fatTime The time field from a directory entry. + */ +void SdBaseFile::printFatTime(uint16_t fatTime) { + printFatTime(SdFat::stdOut(), fatTime); +} +//------------------------------------------------------------------------------ +/** %Print a directory time field. + * + * Format is hh:mm:ss. + * + * \param[in] pr Print stream for output. + * \param[in] fatTime The time field from a directory entry. + */ +void SdBaseFile::printFatTime(Print* pr, uint16_t fatTime) { + print2u(pr, FAT_HOUR(fatTime)); + pr->write(':'); + print2u(pr, FAT_MINUTE(fatTime)); + pr->write(':'); + print2u(pr, FAT_SECOND(fatTime)); +} +//------------------------------------------------------------------------------ +/** Print a file's modify date and time + * + * \param[in] pr Print stream for output. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printModifyDateTime(Print* pr) { + dir_t dir; + if (!dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + printFatDate(pr, dir.lastWriteDate); + pr->write(' '); + printFatTime(pr, dir.lastWriteTime); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Print a file's name + * + * \param[in] pr Print stream for output. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printName(Print* pr) { + char name[13]; + if (!getFilename(name)) { + DBG_FAIL_MACRO; + goto fail; + } +#ifdef COMPAT_PRE1 + pr->print(name); + return true; +#else + return pr->print(name) > 0; +#endif + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Print a file's name to stdOut + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::printName() { + return printName(SdFat::stdOut()); +} +//------------------------------------------------------------------------------ +/** Read the next byte from a file. + * + * \return For success read returns the next byte in the file as an int. + * If an error occurs or end of file is reached -1 is returned. + */ +int16_t SdBaseFile::read() { + uint8_t b; + return read(&b, 1) == 1 ? b : -1; +} +//------------------------------------------------------------------------------ +/** Read data from a file starting at the current position. + * + * \param[out] buf Pointer to the location that will receive the data. + * + * \param[in] nbyte Maximum number of bytes to read. + * + * \return For success read() returns the number of bytes read. + * A value less than \a nbyte, including zero, will be returned + * if end of file is reached. + * If an error occurs, read() returns -1. Possible errors include + * read() called before a file has been opened, corrupt file system + * or an I/O error occurred. + */ +int16_t SdBaseFile::read(void* buf, uint16_t nbyte) { + uint8_t* dst = reinterpret_cast(buf); + uint16_t offset; + uint16_t toRead; + uint32_t block; // raw device block number + + // error if not open or write only + if (!isOpen() || !(flags_ & O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + // max bytes left in file + if (nbyte >= (fileSize_ - curPosition_)) { + nbyte = fileSize_ - curPosition_; + } + // amount left to read + toRead = nbyte; + while (toRead > 0) { + offset = curPosition_ & 0X1FF; // offset in block + if (type_ == FAT_FILE_TYPE_ROOT_FIXED) { + block = vol_->rootDirStart() + (curPosition_ >> 9); + } else { + uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); + if (offset == 0 && blockOfCluster == 0) { + // start of new cluster + if (curPosition_ == 0) { + // use first cluster in file + curCluster_ = firstCluster_; + } else { + // get next cluster from FAT + if (!vol_->fatGet(curCluster_, &curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; + } + uint16_t n = toRead; + + // amount to be read from current block + if (n > (512 - offset)) n = 512 - offset; + + // no buffering needed if n == 512 + if (n == 512 && block != vol_->cacheBlockNumber()) { + if (!vol_->readBlock(block, dst)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // read block to cache and copy data to caller + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + uint8_t* src = vol_->cache()->data + offset; + memcpy(dst, src, n); + } + dst += n; + curPosition_ += n; + toRead -= n; + } + return nbyte; + + fail: + return -1; +} +//------------------------------------------------------------------------------ +/** Read the next directory entry from a directory file. + * + * \param[out] dir The dir_t struct that will receive the data. + * + * \return For success readDir() returns the number of bytes read. + * A value of zero will be returned if end of file is reached. + * If an error occurs, readDir() returns -1. Possible errors include + * readDir() called before a directory has been opened, this is not + * a directory file or an I/O error occurred. + */ +int8_t SdBaseFile::readDir(dir_t* dir) { + int16_t n; + // if not a directory file or miss-positioned return an error + if (!isDir() || (0X1F & curPosition_)) return -1; + + while (1) { + n = read(dir, sizeof(dir_t)); + if (n != sizeof(dir_t)) return n == 0 ? 0 : -1; + // last entry if DIR_NAME_FREE + if (dir->name[0] == DIR_NAME_FREE) return 0; + // skip empty entries and entry for . and .. + if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue; + // return if normal file or subdirectory + if (DIR_IS_FILE_OR_SUBDIR(dir)) return n; + } +} +//------------------------------------------------------------------------------ +// Read next directory entry into the cache +// Assumes file is correctly positioned +dir_t* SdBaseFile::readDirCache() { + uint8_t i; + // error if not directory + if (!isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // index of entry in cache + i = (curPosition_ >> 5) & 0XF; + + // use read to locate and cache block + if (read() < 0) { + DBG_FAIL_MACRO; + goto fail; + } + // advance to next entry + curPosition_ += 31; + + // return pointer to entry + return vol_->cache()->dir + i; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +/** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file read-only, is a directory, + * or an I/O error occurred. + */ +bool SdBaseFile::remove() { + dir_t* d; + // free any clusters - will fail if read-only or directory + if (!truncate(0)) { + DBG_FAIL_MACRO; + goto fail; + } + // cache directory entry + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // mark entry deleted + d->name[0] = DIR_NAME_DELETED; + + // set this file closed + type_ = FAT_FILE_TYPE_CLOSED; + + // write entry to SD + return vol_->cacheFlush(); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \param[in] dirFile The directory that contains the file. + * \param[in] path Path for the file to be removed. + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file is a directory, is read only, + * \a dirFile is not a directory, \a path is not found + * or an I/O error occurred. + */ +bool SdBaseFile::remove(SdBaseFile* dirFile, const char* path) { + SdBaseFile file; + if (!file.open(dirFile, path, O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + return file.remove(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Rename a file or subdirectory. + * + * \param[in] dirFile Directory for the new path. + * \param[in] newPath New path name for the file/directory. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include \a dirFile is not open or is not a directory + * file, newPath is invalid or already exists, or an I/O error occurs. + */ +bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) { + dir_t entry; + uint32_t dirCluster = 0; + SdBaseFile file; + dir_t* d; + + // must be an open file or subdirectory + if (!(isFile() || isSubDir())) { + DBG_FAIL_MACRO; + goto fail; + } + // can't move file + if (vol_ != dirFile->vol_) { + DBG_FAIL_MACRO; + goto fail; + } + // sync() and cache directory entry + sync(); + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // save directory entry + memcpy(&entry, d, sizeof(entry)); + + // mark entry deleted + d->name[0] = DIR_NAME_DELETED; + + // make directory entry for new path + if (isFile()) { + if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) { + goto restore; + } + } else { + // don't create missing path prefix components + if (!file.mkdir(dirFile, newPath, false)) { + goto restore; + } + // save cluster containing new dot dot + dirCluster = file.firstCluster_; + } + // change to new directory entry + dirBlock_ = file.dirBlock_; + dirIndex_ = file.dirIndex_; + + // mark closed to avoid possible destructor close call + file.type_ = FAT_FILE_TYPE_CLOSED; + + // cache new directory entry + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // copy all but name field to new directory entry + memcpy(&d->attributes, &entry.attributes, sizeof(entry) - sizeof(d->name)); + + // update dot dot if directory + if (dirCluster) { + // get new dot dot + uint32_t block = vol_->clusterStartBlock(dirCluster); + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + memcpy(&entry, &vol_->cache()->dir[1], sizeof(entry)); + + // free unused cluster + if (!vol_->freeChain(dirCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // store new dot dot + block = vol_->clusterStartBlock(firstCluster_); + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + memcpy(&vol_->cache()->dir[1], &entry, sizeof(entry)); + } + return vol_->cacheFlush(); + + restore: + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // restore entry + d->name[0] = entry.name[0]; + vol_->cacheFlush(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Remove a directory file. + * + * The directory file will be removed only if it is empty and is not the + * root directory. rmdir() follows DOS and Windows and ignores the + * read-only attribute for the directory. + * + * \note This function should not be used to delete the 8.3 version of a + * directory that has a long name. For example if a directory has the + * long name "New folder" you should not delete the 8.3 name "NEWFOL~1". + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include the file is not a directory, is the root + * directory, is not empty, or an I/O error occurred. + */ +bool SdBaseFile::rmdir() { + // must be open subdirectory + if (!isSubDir()) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + + // make sure directory is empty + while (curPosition_ < fileSize_) { + dir_t* p = readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // done if past last used entry + if (p->name[0] == DIR_NAME_FREE) break; + // skip empty slot, '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; + // error not empty + if (DIR_IS_FILE_OR_SUBDIR(p)) { + DBG_FAIL_MACRO; + goto fail; + } + } + // convert empty directory to normal file for remove + type_ = FAT_FILE_TYPE_NORMAL; + flags_ |= O_WRITE; + return remove(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Recursively delete a directory and all contained files. + * + * This is like the Unix/Linux 'rm -rf *' if called with the root directory + * hence the name. + * + * Warning - This will remove all contents of the directory including + * subdirectories. The directory will then be removed if it is not root. + * The read-only attribute for files will be ignored. + * + * \note This function should not be used to delete the 8.3 version of + * a directory that has a long name. See remove() and rmdir(). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::rmRfStar() { + uint16_t index; + SdBaseFile f; + rewind(); + while (curPosition_ < fileSize_) { + // remember position + index = curPosition_/32; + + dir_t* p = readDirCache(); + if (!p) { + DBG_FAIL_MACRO; + goto fail; + } + // done if past last entry + if (p->name[0] == DIR_NAME_FREE) break; + + // skip empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; + + // skip if part of long file name or volume label in root + if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; + + if (!f.open(this, index, O_READ)) { + DBG_FAIL_MACRO; + goto fail; + } + if (f.isSubDir()) { + // recursively delete + if (!f.rmRfStar()) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // ignore read-only + f.flags_ |= O_WRITE; + if (!f.remove()) { + DBG_FAIL_MACRO; + goto fail; + } + } + // position to next entry if required + if (curPosition_ != (32*(index + 1))) { + if (!seekSet(32*(index + 1))) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + // don't try to delete root + if (!isRoot()) { + if (!rmdir()) { + DBG_FAIL_MACRO; + goto fail; + } + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Create a file object and open it in the current working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). + */ +SdBaseFile::SdBaseFile(const char* path, uint8_t oflag) { + type_ = FAT_FILE_TYPE_CLOSED; + writeError = false; + open(path, oflag); +} +//------------------------------------------------------------------------------ +/** Sets a file's position. + * + * \param[in] pos The new position in bytes from the beginning of the file. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::seekSet(uint32_t pos) { + uint32_t nCur; + uint32_t nNew; + // error if file not open or seek past end of file + if (!isOpen() || pos > fileSize_) { + DBG_FAIL_MACRO; + goto fail; + } + if (type_ == FAT_FILE_TYPE_ROOT_FIXED) { + curPosition_ = pos; + goto done; + } + if (pos == 0) { + // set position to start of file + curCluster_ = 0; + curPosition_ = 0; + goto done; + } + // calculate cluster index for cur and new position + nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); + nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); + + if (nNew < nCur || curPosition_ == 0) { + // must follow chain from first cluster + curCluster_ = firstCluster_; + } else { + // advance from curPosition + nNew -= nCur; + } + while (nNew--) { + if (!vol_->fatGet(curCluster_, &curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + } + curPosition_ = pos; + + done: + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +void SdBaseFile::setpos(fpos_t* pos) { + curPosition_ = pos->position; + curCluster_ = pos->cluster; +} +//------------------------------------------------------------------------------ +/** The sync() call causes all modified data and directory fields + * to be written to the storage device. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include a call to sync() before a file has been + * opened or an I/O error. + */ +bool SdBaseFile::sync() { + // only allow open files and directories + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (flags_ & F_FILE_DIR_DIRTY) { + dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + // check for deleted by another open file object + if (!d || d->name[0] == DIR_NAME_DELETED) { + DBG_FAIL_MACRO; + goto fail; + } + // do not set filesize for dir files + if (!isDir()) d->fileSize = fileSize_; + + // update first cluster fields + d->firstClusterLow = firstCluster_ & 0XFFFF; + d->firstClusterHigh = firstCluster_ >> 16; + + // set modify time if user supplied a callback date/time function + if (dateTime_) { + dateTime_(&d->lastWriteDate, &d->lastWriteTime); + d->lastAccessDate = d->lastWriteDate; + } + // clear directory dirty + flags_ &= ~F_FILE_DIR_DIRTY; + } + return vol_->cacheFlush(); + + fail: + writeError = true; + return false; +} +//------------------------------------------------------------------------------ +/** Copy a file's timestamps + * + * \param[in] file File to copy timestamps from. + * + * \note + * Modify and access timestamps may be overwritten if a date time callback + * function has been set by dateTimeCallback(). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::timestamp(SdBaseFile* file) { + dir_t* d; + dir_t dir; + + // get timestamps + if (!file->dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + // update directory fields + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + // copy timestamps + d->lastAccessDate = dir.lastAccessDate; + d->creationDate = dir.creationDate; + d->creationTime = dir.creationTime; + d->creationTimeTenths = dir.creationTimeTenths; + d->lastWriteDate = dir.lastWriteDate; + d->lastWriteTime = dir.lastWriteTime; + + // write back entry + return vol_->cacheFlush(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Set a file's timestamps in its directory entry. + * + * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive + * OR of flags from the following list + * + * T_ACCESS - Set the file's last access date. + * + * T_CREATE - Set the file's creation date and time. + * + * T_WRITE - Set the file's last write/modification date and time. + * + * \param[in] year Valid range 1980 - 2107 inclusive. + * + * \param[in] month Valid range 1 - 12 inclusive. + * + * \param[in] day Valid range 1 - 31 inclusive. + * + * \param[in] hour Valid range 0 - 23 inclusive. + * + * \param[in] minute Valid range 0 - 59 inclusive. + * + * \param[in] second Valid range 0 - 59 inclusive + * + * \note It is possible to set an invalid date since there is no check for + * the number of days in a month. + * + * \note + * Modify and access timestamps may be overwritten if a date time callback + * function has been set by dateTimeCallback(). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, + uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { + uint16_t dirDate; + uint16_t dirTime; + dir_t* d; + + if (!isOpen() + || year < 1980 + || year > 2107 + || month < 1 + || month > 12 + || day < 1 + || day > 31 + || hour > 23 + || minute > 59 + || second > 59) { + DBG_FAIL_MACRO; + goto fail; + } + // update directory entry + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) { + DBG_FAIL_MACRO; + goto fail; + } + dirDate = FAT_DATE(year, month, day); + dirTime = FAT_TIME(hour, minute, second); + if (flags & T_ACCESS) { + d->lastAccessDate = dirDate; + } + if (flags & T_CREATE) { + d->creationDate = dirDate; + d->creationTime = dirTime; + // seems to be units of 1/100 second not 1/10 as Microsoft states + d->creationTimeTenths = second & 1 ? 100 : 0; + } + if (flags & T_WRITE) { + d->lastWriteDate = dirDate; + d->lastWriteTime = dirTime; + } + return vol_->cacheFlush(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Truncate a file to a specified length. The current file position + * will be maintained if it is less than or equal to \a length otherwise + * it will be set to end of file. + * + * \param[in] length The desired length for the file. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * Reasons for failure include file is read only, file is a directory, + * \a length is greater than the current file size or an I/O error occurs. + */ +bool SdBaseFile::truncate(uint32_t length) { + uint32_t newPos; + // error if not a normal file or read-only + if (!isFile() || !(flags_ & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // error if length is greater than current size + if (length > fileSize_) { + DBG_FAIL_MACRO; + goto fail; + } + // fileSize and length are zero - nothing to do + if (fileSize_ == 0) return true; + + // remember position for seek after truncation + newPos = curPosition_ > length ? length : curPosition_; + + // position to last cluster in truncated file + if (!seekSet(length)) { + DBG_FAIL_MACRO; + goto fail; + } + if (length == 0) { + // free all clusters + if (!vol_->freeChain(firstCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + firstCluster_ = 0; + } else { + uint32_t toFree; + if (!vol_->fatGet(curCluster_, &toFree)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!vol_->isEOC(toFree)) { + // free extra clusters + if (!vol_->freeChain(toFree)) { + DBG_FAIL_MACRO; + goto fail; + } + // current cluster is end of chain + if (!vol_->fatPutEOC(curCluster_)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + fileSize_ = length; + + // need to update directory entry + flags_ |= F_FILE_DIR_DIRTY; + + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // set file to correct position + return seekSet(newPos); + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Write data to an open file. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buf Pointer to the location of the data to be written. + * + * \param[in] nbyte Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a nbyte. If an error occurs, write() returns -1. Possible errors + * include write() is called before a file has been opened, write is called + * for a read-only file, device is full, a corrupt file system or an I/O error. + * + */ +int16_t SdBaseFile::write(const void* buf, uint16_t nbyte) { + // convert void* to uint8_t* - must be before goto statements + const uint8_t* src = reinterpret_cast(buf); + + // number of bytes left to write - must be before goto statements + uint16_t nToWrite = nbyte; + + // error if not a normal file or is read-only + if (!isFile() || !(flags_ & O_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + + // seek to end of file if append flag + if ((flags_ & O_APPEND) && curPosition_ != fileSize_) { + if (!seekEnd()) { + DBG_FAIL_MACRO; + goto fail; + } + } + + while (nToWrite > 0) { + uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); + uint16_t blockOffset = curPosition_ & 0X1FF; + if (blockOfCluster == 0 && blockOffset == 0) { + // start of new cluster + if (curCluster_ != 0) { + uint32_t next; + if (!vol_->fatGet(curCluster_, &next)) { + DBG_FAIL_MACRO; + goto fail; + } + if (vol_->isEOC(next)) { + // add cluster if at end of chain + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + curCluster_ = next; + } + } else { + if (firstCluster_ == 0) { + // allocate first cluster of file + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + curCluster_ = firstCluster_; + } + } + } + // max space in block + uint16_t n = 512 - blockOffset; + + // lesser of space and amount to write + if (n > nToWrite) n = nToWrite; + + // block for data write + uint32_t block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; + if (n == 512) { + // full block - don't need to use cache + if (vol_->cacheBlockNumber() == block) { + // invalidate cache if block is in cache + vol_->cacheSetBlockNumber(0XFFFFFFFF, false); + } + if (!vol_->writeBlock(block, src)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + if (blockOffset == 0 && curPosition_ >= fileSize_) { + // start of new block don't need to read into cache + if (!vol_->cacheFlush()) { + DBG_FAIL_MACRO; + goto fail; + } + // set cache dirty and SD address of block + vol_->cacheSetBlockNumber(block, true); + } else { + // rewrite part of block + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + } + uint8_t* dst = vol_->cache()->data + blockOffset; + memcpy(dst, src, n); + } + curPosition_ += n; + src += n; + nToWrite -= n; + } + if (curPosition_ > fileSize_) { + // update fileSize and insure sync will update dir entry + fileSize_ = curPosition_; + flags_ |= F_FILE_DIR_DIRTY; + } else if (dateTime_ && nbyte) { + // insure sync will update modified date and time + flags_ |= F_FILE_DIR_DIRTY; + } + + if (flags_ & O_SYNC) { + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + return nbyte; + + fail: + // return for write error + writeError = true; + return -1; +} +//------------------------------------------------------------------------------ +// suppress cpplint warnings with NOLINT comment +#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) +void (*SdBaseFile::oldDateTime_)(uint16_t& date, uint16_t& time) = 0; // NOLINT +#endif // ALLOW_DEPRECATED_FUNCTIONS + +// ============== Sd2Card.cpp ============= + +//============================================================================== +// SPI functions +#ifndef SOFTWARE_SPI +// functions for hardware SPI +//------------------------------------------------------------------------------ +// make sure SPCR rate is in expected bits +#if (SPR0 != 0 || SPR1 != 1) +#error unexpected SPCR bits +#endif +//------------------------------------------------------------------------------ +/** + * initialize SPI pins + */ +static void spiBegin() { + pinMode(MISO_PIN, INPUT); + pinMode(MOSI_PIN, OUTPUT); + pinMode(SCK_PIN, OUTPUT); + // SS must be in output mode even it is not chip select + pinMode(SDSS, OUTPUT); + // set SS high - may be chip select for another SPI device +#if SET_SPI_SS_HIGH + digitalWrite(SDSS, HIGH); +#endif // SET_SPI_SS_HIGH +} +//------------------------------------------------------------------------------ +/** + * Initialize hardware SPI + * Set SCK rate to F_CPU/pow(2, 1 + spiRate) for spiRate [0,6] + */ +static void spiInit(uint8_t spiRate) { + // See avr processor documentation + SPCR = (1 << SPE) | (1 << MSTR) | (spiRate >> 1); + SPSR = spiRate & 1 || spiRate == 6 ? 0 : 1 << SPI2X; +} +//------------------------------------------------------------------------------ +/** SPI receive a byte */ +static uint8_t spiRec() { + SPDR = 0XFF; + while (!(SPSR & (1 << SPIF))); + return SPDR; +} +//------------------------------------------------------------------------------ +/** SPI read data - only one call so force inline */ +static inline __attribute__((always_inline)) + void spiRead(uint8_t* buf, uint16_t nbyte) { + if (nbyte-- == 0) return; + SPDR = 0XFF; + for (uint16_t i = 0; i < nbyte; i++) { + while (!(SPSR & (1 << SPIF))); + buf[i] = SPDR; + SPDR = 0XFF; + } + while (!(SPSR & (1 << SPIF))); + buf[nbyte] = SPDR; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +static void spiSend(uint8_t b) { + SPDR = b; + while (!(SPSR & (1 << SPIF))); +} +//------------------------------------------------------------------------------ +/** SPI send block - only one call so force inline */ +static inline __attribute__((always_inline)) + void spiSendBlock(uint8_t token, const uint8_t* buf) { + SPDR = token; + for (uint16_t i = 0; i < 512; i += 2) { + while (!(SPSR & (1 << SPIF))); + SPDR = buf[i]; + while (!(SPSR & (1 << SPIF))); + SPDR = buf[i + 1]; + } + while (!(SPSR & (1 << SPIF))); +} +//------------------------------------------------------------------------------ +#else // SOFTWARE_SPI +#include +static +SoftSPI softSpiBus; +//------------------------------------------------------------------------------ +/** + * initialize SPI pins + */ +static void spiBegin() { + softSpiBus.begin(); +} +//------------------------------------------------------------------------------ +/** Soft SPI receive byte */ +static uint8_t spiRec() { + return softSpiBus.receive(); +} +//------------------------------------------------------------------------------ +/** Soft SPI read data */ +static void spiRead(uint8_t* buf, uint16_t nbyte) { + for (uint16_t i = 0; i < nbyte; i++) { + buf[i] = spiRec(); + } +} +//------------------------------------------------------------------------------ +/** Soft SPI send byte */ +static void spiSend(uint8_t data) { + softSpiBus.send(data); +} +//------------------------------------------------------------------------------ +/** Soft SPI send block */ +static void spiSendBlock(uint8_t token, const uint8_t* buf) { + spiSend(token); + for (uint16_t i = 0; i < 512; i++) { + spiSend(buf[i]); + } +} +#endif // SOFTWARE_SPI +//============================================================================== +// CRC functions +//------------------------------------------------------------------------------ +static uint8_t CRC7(const uint8_t* data, uint8_t n) { + uint8_t crc = 0; + for (uint8_t i = 0; i < n; i++) { + uint8_t d = data[i]; + for (uint8_t j = 0; j < 8; j++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) crc ^= 0x09; + d <<= 1; + } + } + return (crc << 1) | 1; +} +//------------------------------------------------------------------------------ +#if USE_SD_CRC == 1 +// slower CRC-CCITT +// uses the x^16,x^12,x^5,x^1 polynomial. +static uint16_t CRC_CCITT(const uint8_t *data, uint16_t n) { + uint16_t crc = 0; + for (uint16_t i = 0; i < n; i++) { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= data[i]; + crc ^= (uint8_t)(crc & 0xff) >> 4; + crc ^= crc << 12; + crc ^= (crc & 0xff) << 5; + } + return crc; +} +#else // CRC_CCITT +//------------------------------------------------------------------------------ +// faster CRC-CCITT +// uses the x^16,x^12,x^5,x^1 polynomial. +static uint16_t crctab[] PROGMEM = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; +static uint16_t CRC_CCITT(const uint8_t* data, uint16_t n) { + uint16_t crc = 0; + for (uint16_t i = 0; i < n; i++) { + crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0XFF]) ^ (crc << 8); + } + return crc; +} +#endif // CRC_CCITT +//============================================================================== +// Sd2Card member functions +//------------------------------------------------------------------------------ +// send command and return error code. Return zero for OK +uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { + // select card + chipSelectLow(); + + // wait up to 300 ms if busy + waitNotBusy(300); + + uint8_t *pa = reinterpret_cast(&arg); + +#if USE_SD_CRC + // form message + uint8_t d[6] = {cmd | 0X40, pa[3], pa[2], pa[1], pa[0]}; + + // add crc + d[5] = CRC7(d, 5); + + // send message + for (uint8_t k = 0; k < 6; k++) spiSend(d[k]); +#else // USE_SD_CRC + // send command + spiSend(cmd | 0x40); + + // send argument + for (int8_t i = 3; i >= 0; i--) spiSend(pa[i]); + + // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA + spiSend(cmd == CMD0 ? 0X95 : 0X87); +#endif // USE_SD_CRC + + // skip stuff byte for stop read + if (cmd == CMD12) spiRec(); + + // wait for response + for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++); + return status_; +} +//------------------------------------------------------------------------------ +/** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte data blocks in the card + * or zero if an error occurs. + */ +uint32_t Sd2Card::cardSize() { + csd_t csd; + if (!readCSD(&csd)) return 0; + if (csd.v1.csd_ver == 0) { + uint8_t read_bl_len = csd.v1.read_bl_len; + uint16_t c_size = (csd.v1.c_size_high << 10) + | (csd.v1.c_size_mid << 2) | csd.v1.c_size_low; + uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1) + | csd.v1.c_size_mult_low; + return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7); + } else if (csd.v2.csd_ver == 1) { + uint32_t c_size = 0X10000L * csd.v2.c_size_high + 0X100L + * (uint32_t)csd.v2.c_size_mid + csd.v2.c_size_low; + return (c_size + 1) << 10; + } else { + error(SD_CARD_ERROR_BAD_CSD); + return 0; + } +} +//------------------------------------------------------------------------------ +void Sd2Card::chipSelectHigh() { + digitalWrite(chipSelectPin_, HIGH); + // insure MISO goes high impedance + spiSend(0XFF); +} +//------------------------------------------------------------------------------ +void Sd2Card::chipSelectLow() { +#ifndef SOFTWARE_SPI + spiInit(spiRate_); +#endif // SOFTWARE_SPI + digitalWrite(chipSelectPin_, LOW); +} +//------------------------------------------------------------------------------ +/** Erase a range of blocks. + * + * \param[in] firstBlock The address of the first block in the range. + * \param[in] lastBlock The address of the last block in the range. + * + * \note This function requests the SD card to do a flash erase for a + * range of blocks. The data on the card after an erase operation is + * either 0 or 1, depends on the card vendor. The card must support + * single block erase. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { + csd_t csd; + if (!readCSD(&csd)) goto fail; + // check for single block erase + if (!csd.v1.erase_blk_en) { + // erase size mask + uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; + if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { + // error card can't erase specified area + error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + goto fail; + } + } + if (type_ != SD_CARD_TYPE_SDHC) { + firstBlock <<= 9; + lastBlock <<= 9; + } + if (cardCommand(CMD32, firstBlock) + || cardCommand(CMD33, lastBlock) + || cardCommand(CMD38, 0)) { + error(SD_CARD_ERROR_ERASE); + goto fail; + } + if (!waitNotBusy(SD_ERASE_TIMEOUT)) { + error(SD_CARD_ERROR_ERASE_TIMEOUT); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Determine if card supports single block erase. + * + * \return The value one, true, is returned if single block erase is supported. + * The value zero, false, is returned if single block erase is not supported. + */ +bool Sd2Card::eraseSingleBlockEnable() { + csd_t csd; + return readCSD(&csd) ? csd.v1.erase_blk_en : false; +} +//------------------------------------------------------------------------------ +/** + * Initialize an SD flash memory card. + * + * \param[in] sckRateID SPI clock rate selector. See setSckRate(). + * \param[in] chipSelectPin SD chip select pin number. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. The reason for failure + * can be determined by calling errorCode() and errorData(). + */ +bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { + errorCode_ = type_ = 0; + chipSelectPin_ = chipSelectPin; + // 16-bit init start time allows over a minute + uint16_t t0 = (uint16_t)millis(); + uint32_t arg; + + pinMode(chipSelectPin_, OUTPUT); + digitalWrite(chipSelectPin_, HIGH); + spiBegin(); + +#ifndef SOFTWARE_SPI + // set SCK rate for initialization commands + spiRate_ = SPI_SD_INIT_RATE; + spiInit(spiRate_); +#endif // SOFTWARE_SPI + + // must supply min of 74 clock cycles with CS high. + for (uint8_t i = 0; i < 10; i++) spiSend(0XFF); + + // command to go idle in SPI mode + while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { + if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { + error(SD_CARD_ERROR_CMD0); + goto fail; + } + } +#if USE_SD_CRC + if (cardCommand(CMD59, 1) != R1_IDLE_STATE) { + error(SD_CARD_ERROR_CMD59); + goto fail; + } +#endif // USE_SD_CRC + // check SD version + if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) { + type(SD_CARD_TYPE_SD1); + } else { + // only need last byte of r7 response + for (uint8_t i = 0; i < 4; i++) status_ = spiRec(); + if (status_ != 0XAA) { + error(SD_CARD_ERROR_CMD8); + goto fail; + } + type(SD_CARD_TYPE_SD2); + } + // initialize card and send host supports SDHC if SD2 + arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; + + while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { + // check for timeout + if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { + error(SD_CARD_ERROR_ACMD41); + goto fail; + } + } + // if SD2 read OCR register to check for SDHC card + if (type() == SD_CARD_TYPE_SD2) { + if (cardCommand(CMD58, 0)) { + error(SD_CARD_ERROR_CMD58); + goto fail; + } + if ((spiRec() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC); + // discard rest of ocr - contains allowed voltage range + for (uint8_t i = 0; i < 3; i++) spiRec(); + } + chipSelectHigh(); + +#ifndef SOFTWARE_SPI + return setSckRate(sckRateID); +#else // SOFTWARE_SPI + return true; +#endif // SOFTWARE_SPI + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** + * Read a 512 byte block from an SD card. + * + * \param[in] blockNumber Logical block to be read. + * \param[out] dst Pointer to the location that will receive the data. + + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) { + // use address if not SDHC card + if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; + if (cardCommand(CMD17, blockNumber)) { + error(SD_CARD_ERROR_CMD17); + goto fail; + } + return readData(dst, 512); + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Read one data block in a multiple block read sequence + * + * \param[in] dst Pointer to the location for the data to be read. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::readData(uint8_t *dst) { + chipSelectLow(); + return readData(dst, 512); +} +//------------------------------------------------------------------------------ +bool Sd2Card::readData(uint8_t* dst, uint16_t count) { + uint16_t crc; + // wait for start block token + uint16_t t0 = millis(); + while ((status_ = spiRec()) == 0XFF) { + if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) { + error(SD_CARD_ERROR_READ_TIMEOUT); + goto fail; + } + } + if (status_ != DATA_START_BLOCK) { + error(SD_CARD_ERROR_READ); + goto fail; + } + // transfer data + spiRead(dst, count); + // get crc + crc = (spiRec() << 8) | spiRec(); +#if USE_SD_CRC + if (crc != CRC_CCITT(dst, count)) { + error(SD_CARD_ERROR_READ_CRC); + goto fail; + } +#endif // USE_SD_CRC + + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** read CID or CSR register */ +bool Sd2Card::readRegister(uint8_t cmd, void* buf) { + uint8_t* dst = reinterpret_cast(buf); + if (cardCommand(cmd, 0)) { + error(SD_CARD_ERROR_READ_REG); + goto fail; + } + return readData(dst, 16); + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Start a read multiple blocks sequence. + * + * \param[in] blockNumber Address of first block in sequence. + * + * \note This function is used with readData() and readStop() for optimized + * multiple block reads. SPI chipSelect must be low for the entire sequence. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::readStart(uint32_t blockNumber) { + if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; + if (cardCommand(CMD18, blockNumber)) { + error(SD_CARD_ERROR_CMD18); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** End a read multiple blocks sequence. + * +* \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::readStop() { + chipSelectLow(); + if (cardCommand(CMD12, 0)) { + error(SD_CARD_ERROR_CMD12); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** + * Set the SPI clock rate. + * + * \param[in] sckRateID A value in the range [0, 6]. + * + * The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum + * SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128 + * for \a scsRateID = 6. + * + * \return The value one, true, is returned for success and the value zero, + * false, is returned for an invalid value of \a sckRateID. + */ +bool Sd2Card::setSckRate(uint8_t sckRateID) { + if (sckRateID > 6) { + error(SD_CARD_ERROR_SCK_RATE); + return false; + } + spiRate_ = sckRateID; + return true; +} +//------------------------------------------------------------------------------ +// wait for card to go not busy +bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) { + uint16_t t0 = millis(); + while (spiRec() != 0XFF) { + if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** + * Writes a 512 byte block to an SD card. + * + * \param[in] blockNumber Logical block to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) { + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; + if (cardCommand(CMD24, blockNumber)) { + error(SD_CARD_ERROR_CMD24); + goto fail; + } + if (!writeData(DATA_START_BLOCK, src)) goto fail; + + // wait for flash programming to complete + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + error(SD_CARD_ERROR_WRITE_TIMEOUT); + goto fail; + } + // response is r2 so get and check two bytes for nonzero + if (cardCommand(CMD13, 0) || spiRec()) { + error(SD_CARD_ERROR_WRITE_PROGRAMMING); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Write one data block in a multiple block write sequence + * \param[in] src Pointer to the location of the data to be written. + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::writeData(const uint8_t* src) { + chipSelectLow(); + // wait for previous write to finish + if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; + if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto fail; + chipSelectHigh(); + return true; + + fail: + error(SD_CARD_ERROR_WRITE_MULTIPLE); + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +// send one block of data for write block or write multiple blocks +bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { +#if USE_SD_CRC + uint16_t crc = CRC_CCITT(src, 512); +#else // USE_SD_CRC + uint16_t crc = 0XFFFF; +#endif // USE_SD_CRC + + spiSendBlock(token, src); + spiSend(crc >> 8); + spiSend(crc & 0XFF); + + status_ = spiRec(); + if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) { + error(SD_CARD_ERROR_WRITE); + goto fail; + } + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Start a write multiple blocks sequence. + * + * \param[in] blockNumber Address of first block in sequence. + * \param[in] eraseCount The number of blocks to be pre-erased. + * + * \note This function is used with writeData() and writeStop() + * for optimized multiple block writes. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { + // send pre-erase count + if (cardAcmd(ACMD23, eraseCount)) { + error(SD_CARD_ERROR_ACMD23); + goto fail; + } + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; + if (cardCommand(CMD25, blockNumber)) { + error(SD_CARD_ERROR_CMD25); + goto fail; + } + chipSelectHigh(); + return true; + + fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** End a write multiple blocks sequence. + * +* \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +bool Sd2Card::writeStop() { + chipSelectLow(); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; + spiSend(STOP_TRAN_TOKEN); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; + chipSelectHigh(); + return true; + + fail: + error(SD_CARD_ERROR_STOP_TRAN); + chipSelectHigh(); + return false; +} + +// =================== SdVolume =================== + +//------------------------------------------------------------------------------ +#if !USE_MULTIPLE_CARDS +// raw block cache +uint32_t SdVolume::cacheBlockNumber_; // current block number +cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card +Sd2Card* SdVolume::sdCard_; // pointer to SD card object +bool SdVolume::cacheDirty_; // cacheFlush() will write block if true +uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT +#endif // USE_MULTIPLE_CARDS +//------------------------------------------------------------------------------ +// find a contiguous group of clusters +bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { + // start of group + uint32_t bgnCluster; + // end of group + uint32_t endCluster; + // last cluster of FAT + uint32_t fatEnd = clusterCount_ + 1; + + // flag to save place to start next search + bool setStart; + + // set search start cluster + if (*curCluster) { + // try to make file contiguous + bgnCluster = *curCluster + 1; + + // don't save new start location + setStart = false; + } else { + // start at likely place for free cluster + bgnCluster = allocSearchStart_; + + // save next search start if one cluster + setStart = count == 1; + } + // end of group + endCluster = bgnCluster; + + // search the FAT for free clusters + for (uint32_t n = 0;; n++, endCluster++) { + // can't find space checked all clusters + if (n >= clusterCount_) goto fail; + + // past end - start from beginning of FAT + if (endCluster > fatEnd) { + bgnCluster = endCluster = 2; + } + uint32_t f; + if (!fatGet(endCluster, &f)) goto fail; + + if (f != 0) { + // cluster in use try next cluster as bgnCluster + bgnCluster = endCluster + 1; + } else if ((endCluster - bgnCluster + 1) == count) { + // done - found space + break; + } + } + // mark end of chain + if (!fatPutEOC(endCluster)) goto fail; + + // link clusters + while (endCluster > bgnCluster) { + if (!fatPut(endCluster - 1, endCluster)) goto fail; + endCluster--; + } + if (*curCluster != 0) { + // connect chains + if (!fatPut(*curCluster, bgnCluster)) goto fail; + } + // return first cluster number to caller + *curCluster = bgnCluster; + + // remember possible next free cluster + if (setStart) allocSearchStart_ = bgnCluster + 1; + + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool SdVolume::cacheFlush() { + if (cacheDirty_) { + if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { + goto fail; + } + // mirror FAT tables + if (cacheMirrorBlock_) { + if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) { + goto fail; + } + cacheMirrorBlock_ = 0; + } + cacheDirty_ = 0; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) { + if (cacheBlockNumber_ != blockNumber) { + if (!cacheFlush()) goto fail; + if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) goto fail; + cacheBlockNumber_ = blockNumber; + } + if (dirty) cacheDirty_ = true; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// return the size in bytes of a cluster chain +bool SdVolume::chainSize(uint32_t cluster, uint32_t* size) { + uint32_t s = 0; + do { + if (!fatGet(cluster, &cluster)) goto fail; + s += 512UL << clusterSizeShift_; + } while (!isEOC(cluster)); + *size = s; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// Fetch a FAT entry +bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { + uint32_t lba; + if (cluster > (clusterCount_ + 1)) goto fail; + if (FAT12_SUPPORT && fatType_ == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = fatStartBlock_ + (index >> 9); + if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto fail; + index &= 0X1FF; + uint16_t tmp = cacheBuffer_.data[index]; + index++; + if (index == 512) { + if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) goto fail; + index = 0; + } + tmp |= cacheBuffer_.data[index] << 8; + *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; + return true; + } + if (fatType_ == 16) { + lba = fatStartBlock_ + (cluster >> 8); + } else if (fatType_ == 32) { + lba = fatStartBlock_ + (cluster >> 7); + } else { + goto fail; + } + if (lba != cacheBlockNumber_) { + if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto fail; + } + if (fatType_ == 16) { + *value = cacheBuffer_.fat16[cluster & 0XFF]; + } else { + *value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// Store a FAT entry +bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { + uint32_t lba; + // error if reserved cluster + if (cluster < 2) goto fail; + + // error if not in FAT + if (cluster > (clusterCount_ + 1)) goto fail; + + if (FAT12_SUPPORT && fatType_ == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = fatStartBlock_ + (index >> 9); + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail; + // mirror second FAT + if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; + index &= 0X1FF; + uint8_t tmp = value; + if (cluster & 1) { + tmp = (cacheBuffer_.data[index] & 0XF) | tmp << 4; + } + cacheBuffer_.data[index] = tmp; + index++; + if (index == 512) { + lba++; + index = 0; + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail; + // mirror second FAT + if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; + } + tmp = value >> 4; + if (!(cluster & 1)) { + tmp = ((cacheBuffer_.data[index] & 0XF0)) | tmp >> 4; + } + cacheBuffer_.data[index] = tmp; + return true; + } + if (fatType_ == 16) { + lba = fatStartBlock_ + (cluster >> 8); + } else if (fatType_ == 32) { + lba = fatStartBlock_ + (cluster >> 7); + } else { + goto fail; + } + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail; + // store entry + if (fatType_ == 16) { + cacheBuffer_.fat16[cluster & 0XFF] = value; + } else { + cacheBuffer_.fat32[cluster & 0X7F] = value; + } + // mirror second FAT + if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// free a cluster chain +bool SdVolume::freeChain(uint32_t cluster) { + uint32_t next; + + // clear free cluster location + allocSearchStart_ = 2; + + do { + if (!fatGet(cluster, &next)) goto fail; + + // free cluster + if (!fatPut(cluster, 0)) goto fail; + + cluster = next; + } while (!isEOC(cluster)); + + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +/** Volume free space in clusters. + * + * \return Count of free clusters for success or -1 if an error occurs. + */ +int32_t SdVolume::freeClusterCount() { + uint32_t free = 0; + uint16_t n; + uint32_t todo = clusterCount_ + 2; + + if (fatType_ == 16) { + n = 256; + } else if (fatType_ == 32) { + n = 128; + } else { + // put FAT12 here + return -1; + } + + for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) { + if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1; + if (todo < n) n = todo; + if (fatType_ == 16) { + for (uint16_t i = 0; i < n; i++) { + if (cacheBuffer_.fat16[i] == 0) free++; + } + } else { + for (uint16_t i = 0; i < n; i++) { + if (cacheBuffer_.fat32[i] == 0) free++; + } + } + } + return free; +} +//------------------------------------------------------------------------------ +/** Initialize a FAT volume. + * + * \param[in] dev The SD card where the volume is located. + * + * \param[in] part The partition to be used. Legal values for \a part are + * 1-4 to use the corresponding partition on a device formatted with + * a MBR, Master Boot Record, or zero if the device is formatted as + * a super floppy with the FAT boot sector in block zero. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. Reasons for + * failure include not finding a valid partition, not finding a valid + * FAT file system in the specified partition or an I/O error. + */ +bool SdVolume::init(Sd2Card* dev, uint8_t part) { + uint32_t totalBlocks; + uint32_t volumeStartBlock = 0; + fat32_boot_t* fbs; + + sdCard_ = dev; + fatType_ = 0; + allocSearchStart_ = 2; + cacheDirty_ = 0; // cacheFlush() will write block if true + cacheMirrorBlock_ = 0; + cacheBlockNumber_ = 0XFFFFFFFF; + + // if part == 0 assume super floppy with FAT boot sector in block zero + // if part > 0 assume mbr volume with partition table + if (part) { + if (part > 4)goto fail; + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto fail; + part_t* p = &cacheBuffer_.mbr.part[part-1]; + if ((p->boot & 0X7F) !=0 || + p->totalSectors < 100 || + p->firstSector == 0) { + // not a valid partition + goto fail; + } + volumeStartBlock = p->firstSector; + } + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto fail; + fbs = &cacheBuffer_.fbs32; + if (fbs->bytesPerSector != 512 || + fbs->fatCount == 0 || + fbs->reservedSectorCount == 0 || + fbs->sectorsPerCluster == 0) { + // not valid FAT volume + goto fail; + } + fatCount_ = fbs->fatCount; + blocksPerCluster_ = fbs->sectorsPerCluster; + // determine shift that is same as multiply by blocksPerCluster_ + clusterSizeShift_ = 0; + while (blocksPerCluster_ != (1 << clusterSizeShift_)) { + // error if not power of 2 + if (clusterSizeShift_++ > 7) goto fail; + } + blocksPerFat_ = fbs->sectorsPerFat16 ? + fbs->sectorsPerFat16 : fbs->sectorsPerFat32; + + fatStartBlock_ = volumeStartBlock + fbs->reservedSectorCount; + + // count for FAT16 zero for FAT32 + rootDirEntryCount_ = fbs->rootDirEntryCount; + + // directory start for FAT16 dataStart for FAT32 + rootDirStart_ = fatStartBlock_ + fbs->fatCount * blocksPerFat_; + + // data start for FAT16 and FAT32 + dataStartBlock_ = rootDirStart_ + ((32 * fbs->rootDirEntryCount + 511)/512); + + // total blocks for FAT16 or FAT32 + totalBlocks = fbs->totalSectors16 ? + fbs->totalSectors16 : fbs->totalSectors32; + // total data blocks + clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); + + // divide by cluster size to get cluster count + clusterCount_ >>= clusterSizeShift_; + + // FAT type is determined by cluster count + if (clusterCount_ < 4085) { + fatType_ = 12; + if (!FAT12_SUPPORT) goto fail; + } else if (clusterCount_ < 65525) { + fatType_ = 16; + } else { + rootDirStart_ = fbs->fat32RootCluster; + fatType_ = 32; + } + return true; + + fail: + return false; +} + +// =============== SdFile.cpp ==================== + +/** Create a file object and open it in the current working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). + */ +SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { +} +//------------------------------------------------------------------------------ +/** Write data to an open file. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buf Pointer to the location of the data to be written. + * + * \param[in] nbyte Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a nbyte. If an error occurs, write() returns -1. Possible errors + * include write() is called before a file has been opened, write is called + * for a read-only file, device is full, a corrupt file system or an I/O error. + * + */ +int16_t SdFile::write(const void* buf, uint16_t nbyte) { + return SdBaseFile::write(buf, nbyte); +} +//------------------------------------------------------------------------------ +/** Write a byte to a file. Required by the Arduino Print class. + * \param[in] b the byte to be written. + * Use getWriteError to check for errors. + * \return 1 for success and 0 for failure. + */ +#ifdef COMPAT_PRE1 + void SdFile::write(uint8_t b) { + SdBaseFile::write(&b, 1); + } +#else +size_t SdFile::write(uint8_t b) { + return SdBaseFile::write(&b, 1) == 1 ? 1 : 0; +} +#endif + +//------------------------------------------------------------------------------ +/** Write a string to a file. Used by the Arduino Print class. + * \param[in] str Pointer to the string. + * Use getWriteError to check for errors. + * \return count of characters written for success or -1 for failure. + */ +int16_t SdFile::write(const char* str) { + return SdBaseFile::write(str, strlen(str)); +} +//------------------------------------------------------------------------------ +/** Write a PROGMEM string to a file. + * \param[in] str Pointer to the PROGMEM string. + * Use getWriteError to check for errors. + */ +void SdFile::write_P(PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c); +} +//------------------------------------------------------------------------------ +/** Write a PROGMEM string followed by CR/LF to a file. + * \param[in] str Pointer to the PROGMEM string. + * Use getWriteError to check for errors. + */ +void SdFile::writeln_P(PGM_P str) { + write_P(str); + write_P(PSTR("\r\n")); +} + +// ================ SdFatUtil.cpp =================== + +//------------------------------------------------------------------------------ +/** Amount of free RAM + * \return The number of free bytes. + */ +int SdFatUtil::FreeRam() { + extern int __bss_end; + extern int* __brkval; + int free_memory; + if (reinterpret_cast(__brkval) == 0) { + // if no heap use from end of bss section + free_memory = reinterpret_cast(&free_memory) + - reinterpret_cast(&__bss_end); + } else { + // use from top of stack to heap + free_memory = reinterpret_cast(&free_memory) + - reinterpret_cast(__brkval); + } + return free_memory; +} +//------------------------------------------------------------------------------ +/** %Print a string in flash memory. + * + * \param[in] pr Print object for output. + * \param[in] str Pointer to string stored in flash memory. + */ +void SdFatUtil::print_P(Print* pr, PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) pr->write(c); +} +//------------------------------------------------------------------------------ +/** %Print a string in flash memory followed by a CR/LF. + * + * \param[in] pr Print object for output. + * \param[in] str Pointer to string stored in flash memory. + */ +void SdFatUtil::println_P(Print* pr, PGM_P str) { + print_P(pr, str); + pr->println(); +} +//------------------------------------------------------------------------------ +/** %Print a string in flash memory to Serial. + * + * \param[in] str Pointer to string stored in flash memory. + */ +void SdFatUtil::SerialPrint_P(PGM_P str) { + print_P(SdFat::stdOut(), str); +} +//------------------------------------------------------------------------------ +/** %Print a string in flash memory to Serial followed by a CR/LF. + * + * \param[in] str Pointer to string stored in flash memory. + */ +void SdFatUtil::SerialPrintln_P(PGM_P str) { + println_P(SdFat::stdOut(), str); +} + +// ============== + +#endif // SDSUPPORT + diff --git a/Repetier/SdFat.h b/Repetier/SdFat.h new file mode 100644 index 0000000..4f53bde --- /dev/null +++ b/Repetier/SdFat.h @@ -0,0 +1,2160 @@ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ +/* Arduino SdFat Library + * Copyright (C) 2012 by William Greiman + * + * This file is part of the Arduino SdFat Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino SdFat Library. If not, see + * . + */ + +/** +\mainpage Arduino %SdFat Library +
Copyright © 2012 by William Greiman +
+ +\section Intro Introduction +The Arduino %SdFat Library is a minimal implementation of FAT16 and FAT32 +file systems on SD flash memory cards. Standard SD and high capacity SDHC +cards are supported. + +Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT +nonzero in SdFatConfig.h. + +The %SdFat library only supports short 8.3 names. + +The main classes in %SdFat are SdFat, SdFile, \ref fstream, \ref ifstream, +and \ref ofstream. + +The SdFat class maintains a volume working directories, a current working +directory, and simplifies initialization of other classes. + +The SdFile class provides binary file access functions such as open(), read(), +remove(), write(), close() and sync(). This class supports access to the root +directory and subdirectories. + +The \ref fstream class implements C++ iostreams for both reading and writing +text files. + +The \ref ifstream class implements the C++ iostreams for reading text files. + +The \ref ofstream class implements the C++ iostreams for writing text files. + +The classes \ref ibufstream and \ref obufstream format and parse character + strings in memory buffers. + +the classes ArduinoInStream and ArduinoOutStream provide iostream functions +for Serial, LiquidCrystal, and other devices. + +The SdVolume class supports FAT16 and FAT32 partitions. Most applications +will not need to call SdVolume member function. + +The Sd2Card class supports access to standard SD cards and SDHC cards. Most +applications will not need to call Sd2Card functions. The Sd2Card class can +be used for raw access to the SD card. + +A number of example are provided in the %SdFat/examples folder. These were +developed to test %SdFat and illustrate its use. + +%SdFat was developed for high speed data recording. %SdFat was used to +implement an audio record/play class, WaveRP, for the Adafruit Wave Shield. +This application uses special Sd2Card calls to write to contiguous files in +raw mode. These functions reduce write latency so that audio can be +recorded with the small amount of RAM in the Arduino. + +\section SDcard SD\SDHC Cards + +Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and +most consumer devices use the 4-bit parallel SD protocol. A card that +functions well on A PC or Mac may not work well on the Arduino. + +Most cards have good SPI read performance but cards vary widely in SPI +write performance. Write performance is limited by how efficiently the +card manages internal erase/remapping operations. The Arduino cannot +optimize writes to reduce erase operations because of its limit RAM. + +SanDisk cards generally have good write performance. They seem to have +more internal RAM buffering than other cards and therefore can limit +the number of flash erase operations that the Arduino forces due to its +limited RAM. + +\section Hardware Hardware Configuration + +%SdFat was developed using an + Adafruit Industries + Wave Shield. + +The hardware interface to the SD card should not use a resistor based level +shifter. %SdFat sets the SPI bus frequency to 8 MHz which results in signal +rise times that are too slow for the edge detectors in many newer SD card +controllers when resistor voltage dividers are used. + +The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the +74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield +uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the +74LCX245. + +If you are using a resistor based level shifter and are having problems try +setting the SPI bus frequency to 4 MHz. This can be done by using +card.init(SPI_HALF_SPEED) to initialize the SD card. + +\section comment Bugs and Comments + +If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. + +\section SdFatClass SdFat Usage + +%SdFat uses a slightly restricted form of short names. +Only printable ASCII characters are supported. No characters with code point +values greater than 127 are allowed. Space is not allowed even though space +was allowed in the API of early versions of DOS. + +Short names are limited to 8 characters followed by an optional period (.) +and extension of up to 3 characters. The characters may be any combination +of letters and digits. The following special characters are also allowed: + +$ % ' - _ @ ~ ` ! ( ) { } ^ # & + +Short names are always converted to upper case and their original case +value is lost. + +\note + The Arduino Print class uses character +at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink +function to control when data is written to the SD card. + +\par +An application which writes to a file using print(), println() or +\link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink +at the appropriate time to force data and directory information to be written +to the SD Card. Data and directory information are also written to the SD card +when \link SdFile::close() close() \endlink is called. + +\par +Applications must use care calling \link SdFile::sync() sync() \endlink +since 2048 bytes of I/O is required to update file and +directory information. This includes writing the current data block, reading +the block that contains the directory entry for update, writing the directory +block back and reading back the current data block. + +It is possible to open a file with two or more instances of SdFile. A file may +be corrupted if data is written to the file by more than one instance of SdFile. + +\section HowTo How to format SD Cards as FAT Volumes + +You should use a freshly formatted SD card for best performance. FAT +file systems become slower if many files have been created and deleted. +This is because the directory entry for a deleted file is marked as deleted, +but is not deleted. When a new file is created, these entries must be scanned +before creating the file, a flaw in the FAT design. Also files can become +fragmented which causes reads and writes to be slower. + +A formatter sketch, SdFormatter.pde, is included in the +%SdFat/examples/SdFormatter directory. This sketch attempts to +emulate SD Association's SDFormatter. + +The best way to restore an SD card's format on a PC is to use SDFormatter +which can be downloaded from: + +http://www.sdcard.org/consumers/formatter/ + +SDFormatter aligns flash erase boundaries with file +system structures which reduces write latency and file system overhead. + +SDFormatter does not have an option for FAT type so it may format +small cards as FAT12. + +After the MBR is restored by SDFormatter you may need to reformat small +cards that have been formatted FAT12 to force the volume type to be FAT16. + +If you reformat the SD card with an OS utility, choose a cluster size that +will result in: + +4084 < CountOfClusters && CountOfClusters < 65525 + +The volume will then be FAT16. + +If you are formatting an SD card on OS X or Linux, be sure to use the first +partition. Format this partition with a cluster count in above range for FAT16. +SDHC cards should be formatted FAT32 with a cluster size of 32 KB. + +Microsoft operating systems support removable media formatted with a +Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector +in block zero. + +Microsoft operating systems expect MBR formatted removable media +to have only one partition. The first partition should be used. + +Microsoft operating systems do not support partitioning SD flash cards. +If you erase an SD card with a program like KillDisk, Most versions of +Windows will format the card as a super floppy. + +\section References References + +Adafruit Industries: + +http://www.adafruit.com/ + +http://www.ladyada.net/make/waveshield/ + +The Arduino site: + +http://www.arduino.cc/ + +For more information about FAT file systems see: + +http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + +For information about using SD cards as SPI devices see: + +http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + +The ATmega328 datasheet: + +http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf + + + */ +#ifndef SdFat_h +#define SdFat_h +/** + * \file + * \brief SdFat class + */ +//------------------------------------------------------------------------------ +/** SdFat version YYYYMMDD */ +#define SD_FAT_VERSION 20120719 +//------------------------------------------------------------------------------ +/** error if old IDE */ +#if !defined(ARDUINO) || ARDUINO < 100 +#error Arduino IDE must be 1.0 or greater +#endif // ARDUINO < 100 +//------------------------------------------------------------------------------ +#include +// Based on the document: +// +// SD Specifications +// Part 1 +// Physical Layer +// Simplified Specification +// Version 3.01 +// May 18, 2010 +// +// http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs +//------------------------------------------------------------------------------ +// SD card commands +/** GO_IDLE_STATE - init card in spi mode if CS low */ +uint8_t const CMD0 = 0X00; +/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ +uint8_t const CMD8 = 0X08; +/** SEND_CSD - read the Card Specific Data (CSD register) */ +uint8_t const CMD9 = 0X09; +/** SEND_CID - read the card identification information (CID register) */ +uint8_t const CMD10 = 0X0A; +/** STOP_TRANSMISSION - end multiple block read sequence */ +uint8_t const CMD12 = 0X0C; +/** SEND_STATUS - read the card status register */ +uint8_t const CMD13 = 0X0D; +/** READ_SINGLE_BLOCK - read a single data block from the card */ +uint8_t const CMD17 = 0X11; +/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ +uint8_t const CMD18 = 0X12; +/** WRITE_BLOCK - write a single data block to the card */ +uint8_t const CMD24 = 0X18; +/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ +uint8_t const CMD25 = 0X19; +/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ +uint8_t const CMD32 = 0X20; +/** ERASE_WR_BLK_END - sets the address of the last block of the continuous + range to be erased*/ +uint8_t const CMD33 = 0X21; +/** ERASE - erase all previously selected blocks */ +uint8_t const CMD38 = 0X26; +/** APP_CMD - escape for application specific command */ +uint8_t const CMD55 = 0X37; +/** READ_OCR - read the OCR register of a card */ +uint8_t const CMD58 = 0X3A; +/** CRC_ON_OFF - enable or disable CRC checking */ +uint8_t const CMD59 = 0X3B; +/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be + pre-erased before writing */ +uint8_t const ACMD23 = 0X17; +/** SD_SEND_OP_COMD - Sends host capacity support information and + activates the card's initialization process */ +uint8_t const ACMD41 = 0X29; +//------------------------------------------------------------------------------ +/** status for card in the ready state */ +uint8_t const R1_READY_STATE = 0X00; +/** status for card in the idle state */ +uint8_t const R1_IDLE_STATE = 0X01; +/** status bit for illegal command */ +uint8_t const R1_ILLEGAL_COMMAND = 0X04; +/** start data token for read or write single block*/ +uint8_t const DATA_START_BLOCK = 0XFE; +/** stop token for write multiple blocks*/ +uint8_t const STOP_TRAN_TOKEN = 0XFD; +/** start data token for write multiple blocks*/ +uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; +/** mask for data response tokens after a write block operation */ +uint8_t const DATA_RES_MASK = 0X1F; +/** write data accepted token */ +uint8_t const DATA_RES_ACCEPTED = 0X05; +//------------------------------------------------------------------------------ +/** Card IDentification (CID) register */ +typedef struct CID { + // byte 0 + /** Manufacturer ID */ + unsigned char mid; + // byte 1-2 + /** OEM/Application ID */ + char oid[2]; + // byte 3-7 + /** Product name */ + char pnm[5]; + // byte 8 + /** Product revision least significant digit */ + unsigned char prv_m : 4; + /** Product revision most significant digit */ + unsigned char prv_n : 4; + // byte 9-12 + /** Product serial number */ + uint32_t psn; + // byte 13 + /** Manufacturing date year low digit */ + unsigned char mdt_year_high : 4; + /** not used */ + unsigned char reserved : 4; + // byte 14 + /** Manufacturing date month */ + unsigned char mdt_month : 4; + /** Manufacturing date year low digit */ + unsigned char mdt_year_low :4; + // byte 15 + /** not used always 1 */ + unsigned char always1 : 1; + /** CRC7 checksum */ + unsigned char crc : 7; +}cid_t; +//------------------------------------------------------------------------------ +/** CSD for version 1.00 cards */ +typedef struct CSDV1 { + // byte 0 + unsigned char reserved1 : 6; + unsigned char csd_ver : 2; + // byte 1 + unsigned char taac; + // byte 2 + unsigned char nsac; + // byte 3 + unsigned char tran_speed; + // byte 4 + unsigned char ccc_high; + // byte 5 + unsigned char read_bl_len : 4; + unsigned char ccc_low : 4; + // byte 6 + unsigned char c_size_high : 2; + unsigned char reserved2 : 2; + unsigned char dsr_imp : 1; + unsigned char read_blk_misalign :1; + unsigned char write_blk_misalign : 1; + unsigned char read_bl_partial : 1; + // byte 7 + unsigned char c_size_mid; + // byte 8 + unsigned char vdd_r_curr_max : 3; + unsigned char vdd_r_curr_min : 3; + unsigned char c_size_low :2; + // byte 9 + unsigned char c_size_mult_high : 2; + unsigned char vdd_w_cur_max : 3; + unsigned char vdd_w_curr_min : 3; + // byte 10 + unsigned char sector_size_high : 6; + unsigned char erase_blk_en : 1; + unsigned char c_size_mult_low : 1; + // byte 11 + unsigned char wp_grp_size : 7; + unsigned char sector_size_low : 1; + // byte 12 + unsigned char write_bl_len_high : 2; + unsigned char r2w_factor : 3; + unsigned char reserved3 : 2; + unsigned char wp_grp_enable : 1; + // byte 13 + unsigned char reserved4 : 5; + unsigned char write_partial : 1; + unsigned char write_bl_len_low : 2; + // byte 14 + unsigned char reserved5: 2; + unsigned char file_format : 2; + unsigned char tmp_write_protect : 1; + unsigned char perm_write_protect : 1; + unsigned char copy : 1; + /** Indicates the file format on the card */ + unsigned char file_format_grp : 1; + // byte 15 + unsigned char always1 : 1; + unsigned char crc : 7; +}csd1_t; +//------------------------------------------------------------------------------ +/** CSD for version 2.00 cards */ +typedef struct CSDV2 { + // byte 0 + unsigned char reserved1 : 6; + unsigned char csd_ver : 2; + // byte 1 + /** fixed to 0X0E */ + unsigned char taac; + // byte 2 + /** fixed to 0 */ + unsigned char nsac; + // byte 3 + unsigned char tran_speed; + // byte 4 + unsigned char ccc_high; + // byte 5 + /** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */ + unsigned char read_bl_len : 4; + unsigned char ccc_low : 4; + // byte 6 + /** not used */ + unsigned char reserved2 : 4; + unsigned char dsr_imp : 1; + /** fixed to 0 */ + unsigned char read_blk_misalign :1; + /** fixed to 0 */ + unsigned char write_blk_misalign : 1; + /** fixed to 0 - no partial read */ + unsigned char read_bl_partial : 1; + // byte 7 + /** high part of card size */ + unsigned char c_size_high : 6; + /** not used */ + unsigned char reserved3 : 2; + // byte 8 + /** middle part of card size */ + unsigned char c_size_mid; + // byte 9 + /** low part of card size */ + unsigned char c_size_low; + // byte 10 + /** sector size is fixed at 64 KB */ + unsigned char sector_size_high : 6; + /** fixed to 1 - erase single is supported */ + unsigned char erase_blk_en : 1; + /** not used */ + unsigned char reserved4 : 1; + // byte 11 + unsigned char wp_grp_size : 7; + /** sector size is fixed at 64 KB */ + unsigned char sector_size_low : 1; + // byte 12 + /** write_bl_len fixed for 512 byte blocks */ + unsigned char write_bl_len_high : 2; + /** fixed value of 2 */ + unsigned char r2w_factor : 3; + /** not used */ + unsigned char reserved5 : 2; + /** fixed value of 0 - no write protect groups */ + unsigned char wp_grp_enable : 1; + // byte 13 + unsigned char reserved6 : 5; + /** always zero - no partial block read*/ + unsigned char write_partial : 1; + /** write_bl_len fixed for 512 byte blocks */ + unsigned char write_bl_len_low : 2; + // byte 14 + unsigned char reserved7: 2; + /** Do not use always 0 */ + unsigned char file_format : 2; + unsigned char tmp_write_protect : 1; + unsigned char perm_write_protect : 1; + unsigned char copy : 1; + /** Do not use always 0 */ + unsigned char file_format_grp : 1; + // byte 15 + /** not used always 1 */ + unsigned char always1 : 1; + /** checksum */ + unsigned char crc : 7; +}csd2_t; +//------------------------------------------------------------------------------ +/** union of old and new style CSD register */ +union csd_t { + csd1_t v1; + csd2_t v2; +}; + +//------------------------------------------------------------------------------ +/** + * To enable SD card CRC checking set USE_SD_CRC nonzero. + * + * Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function. + * + * Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function. + */ +#define USE_SD_CRC 0 +//------------------------------------------------------------------------------ +/** + * To use multiple SD cards set USE_MULTIPLE_CARDS nonzero. + * + * Using multiple cards costs 400 - 500 bytes of flash. + * + * Each card requires about 550 bytes of SRAM so use of a Mega is recommended. + */ +#define USE_MULTIPLE_CARDS 0 +//------------------------------------------------------------------------------ +/** + * Set nonzero to use Serial (the HardwareSerial class) for error messages + * and output from print functions like ls(). + * + * If USE_SERIAL_FOR_STD_OUT is zero, a small non-interrupt driven class + * is used to output messages to serial port zero. This allows an alternate + * Serial library like SerialPort to be used with SdFat. + * + * You can redirect stdOut with SdFat::setStdOut(Print* stream) and + * get the current stream with SdFat::stdOut(). + */ +#define USE_SERIAL_FOR_STD_OUT 0 +//------------------------------------------------------------------------------ +/** + * Call flush for endl if ENDL_CALLS_FLUSH is nonzero + * + * The standard for iostreams is to call flush. This is very costly for + * SdFat. Each call to flush causes 2048 bytes of I/O to the SD. + * + * SdFat has a single 512 byte buffer for SD I/O so it must write the current + * data block to the SD, read the directory block from the SD, update the + * directory entry, write the directory block to the SD and read the data + * block back into the buffer. + * + * The SD flash memory controller is not designed for this many rewrites + * so performance may be reduced by more than a factor of 100. + * + * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force + * all data to be written to the SD. + */ +#define ENDL_CALLS_FLUSH 0 +//------------------------------------------------------------------------------ +/** + * Allow use of deprecated functions if ALLOW_DEPRECATED_FUNCTIONS is nonzero + */ +#define ALLOW_DEPRECATED_FUNCTIONS 1 +//------------------------------------------------------------------------------ +/** + * Allow FAT12 volumes if FAT12_SUPPORT is nonzero. + * FAT12 has not been well tested. + */ +#define FAT12_SUPPORT 0 +//------------------------------------------------------------------------------ +/** + * SPI init rate for SD initialization commands. Must be 5 (F_CPU/64) + * or 6 (F_CPU/128). + */ +#define SPI_SD_INIT_RATE 5 +//------------------------------------------------------------------------------ +/** + * Set the SS pin high for hardware SPI. If SS is chip select for another SPI + * device this will disable that device during the SD init phase. + */ +#define SET_SPI_SS_HIGH 1 +//------------------------------------------------------------------------------ +/** + * Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos. + * Default pins used are SS 10, MOSI 11, MISO 12, and SCK 13. + * Edit Software Spi pins to change pin numbers. + * + * MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used + * on Mega Arduinos. Software SPI works well with GPS Shield V1.1 + * but many SD cards will fail with GPS Shield V1.0. + */ +#define MEGA_SOFT_SPI 0 +//------------------------------------------------------------------------------ +/** + * Define LEONARDO_SOFT_SPI nonzero to use software SPI on Leonardo Arduinos. + * Derfault pins used are SS 10, MOSI 11, MISO 12, and SCK 13. + * Edit Software Spi pins to change pin numbers. + * + * LEONARDO_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used + * on Leonardo Arduinos. Software SPI works well with GPS Shield V1.1 + * but many SD cards will fail with GPS Shield V1.0. + */ +#define LEONARDO_SOFT_SPI 0 +//------------------------------------------------------------------------------ +/** + * Set USE_SOFTWARE_SPI nonzero to always use software SPI. + */ +#define USE_SOFTWARE_SPI 0 +// define software SPI pins so Mega can use unmodified 168/328 shields +/** Default Software SPI chip select pin */ +uint8_t const SOFT_SPI_CS_PIN = 10; +/** Software SPI Master Out Slave In pin */ +uint8_t const SOFT_SPI_MOSI_PIN = 11; +/** Software SPI Master In Slave Out pin */ +uint8_t const SOFT_SPI_MISO_PIN = 12; +/** Software SPI Clock pin */ +uint8_t const SOFT_SPI_SCK_PIN = 13; +//------------------------------------------------------------------------------ +// SPI speed is F_CPU/2^(1 + index), 0 <= index <= 6 +/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */ +uint8_t const SPI_FULL_SPEED = 0; +/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */ +uint8_t const SPI_HALF_SPEED = 1; +/** Set SCK rate to F_CPU/8. See Sd2Card::setSckRate(). */ +uint8_t const SPI_QUARTER_SPEED = 2; +/** Set SCK rate to F_CPU/16. See Sd2Card::setSckRate(). */ +uint8_t const SPI_EIGHTH_SPEED = 3; +/** Set SCK rate to F_CPU/32. See Sd2Card::setSckRate(). */ +uint8_t const SPI_SIXTEENTH_SPEED = 4; +//------------------------------------------------------------------------------ +/** init timeout ms */ +uint16_t const SD_INIT_TIMEOUT = 2000; +/** erase timeout ms */ +uint16_t const SD_ERASE_TIMEOUT = 10000; +/** read timeout ms */ +uint16_t const SD_READ_TIMEOUT = 300; +/** write time out ms */ +uint16_t const SD_WRITE_TIMEOUT = 600; +//------------------------------------------------------------------------------ +// SD card errors +/** timeout error for command CMD0 (initialize card in SPI mode) */ +uint8_t const SD_CARD_ERROR_CMD0 = 0X1; +/** CMD8 was not accepted - not a valid SD card*/ +uint8_t const SD_CARD_ERROR_CMD8 = 0X2; +/** card returned an error response for CMD12 (write stop) */ +uint8_t const SD_CARD_ERROR_CMD12 = 0X3; +/** card returned an error response for CMD17 (read block) */ +uint8_t const SD_CARD_ERROR_CMD17 = 0X4; +/** card returned an error response for CMD18 (read multiple block) */ +uint8_t const SD_CARD_ERROR_CMD18 = 0X5; +/** card returned an error response for CMD24 (write block) */ +uint8_t const SD_CARD_ERROR_CMD24 = 0X6; +/** WRITE_MULTIPLE_BLOCKS command failed */ +uint8_t const SD_CARD_ERROR_CMD25 = 0X7; +/** card returned an error response for CMD58 (read OCR) */ +uint8_t const SD_CARD_ERROR_CMD58 = 0X8; +/** SET_WR_BLK_ERASE_COUNT failed */ +uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; +/** ACMD41 initialization process timeout */ +uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; +/** card returned a bad CSR version field */ +uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; +/** erase block group command failed */ +uint8_t const SD_CARD_ERROR_ERASE = 0XC; +/** card not capable of single block erase */ +uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; +/** Erase sequence timed out */ +uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; +/** card returned an error token instead of read data */ +uint8_t const SD_CARD_ERROR_READ = 0XF; +/** read CID or CSD failed */ +uint8_t const SD_CARD_ERROR_READ_REG = 0X10; +/** timeout while waiting for start of read data */ +uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; +/** card did not accept STOP_TRAN_TOKEN */ +uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; +/** card returned an error token as a response to a write operation */ +uint8_t const SD_CARD_ERROR_WRITE = 0X13; +/** attempt to write protected block zero */ +uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used +/** card did not go ready for a multiple block write */ +uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; +/** card returned an error to a CMD13 status check after a write */ +uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; +/** timeout occurred during write programming */ +uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; +/** incorrect rate selected */ +uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; +/** init() not called */ +uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; +/** card returned an error for CMD59 (CRC_ON_OFF) */ +uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; +/** invalid read CRC */ +uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; +//------------------------------------------------------------------------------ +// card types +/** Standard capacity V1 SD card */ +uint8_t const SD_CARD_TYPE_SD1 = 1; +/** Standard capacity V2 SD card */ +uint8_t const SD_CARD_TYPE_SD2 = 2; +/** High Capacity SD card */ +uint8_t const SD_CARD_TYPE_SDHC = 3; +/** + * define SOFTWARE_SPI to use bit-bang SPI + */ +//------------------------------------------------------------------------------ +#if LEONARDO_SOFT_SPI && defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY) +#define SOFTWARE_SPI +#elif MEGA_SOFT_SPI&&(defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__)) +#define SOFTWARE_SPI +#elif USE_SOFTWARE_SPI +#define SOFTWARE_SPI +#endif // LEONARDO_SOFT_SPI +//------------------------------------------------------------------------------ +// define default chip select pin +// +#ifndef SOFTWARE_SPI +// hardware pin defs +/** The default chip select pin for the SD card is SS. */ +uint8_t const SD_CHIP_SELECT_PIN = SDSS; +#else // SOFTWARE_SPI +/** SPI chip select pin */ +uint8_t const SD_CHIP_SELECT_PIN = SOFT_SPI_CS_PIN; +#endif // SOFTWARE_SPI +//------------------------------------------------------------------------------ +/** + * \class Sd2Card + * \brief Raw access to SD and SDHC flash memory cards. + */ +class Sd2Card { + public: + /** Construct an instance of Sd2Card. */ + Sd2Card() : errorCode_(SD_CARD_ERROR_INIT_NOT_CALLED), type_(0) {} + uint32_t cardSize(); + bool erase(uint32_t firstBlock, uint32_t lastBlock); + bool eraseSingleBlockEnable(); + /** + * Set SD error code. + * \param[in] code value for error code. + */ + void error(uint8_t code) {errorCode_ = code;} + /** + * \return error code for last error. See Sd2Card.h for a list of error codes. + */ + int errorCode() const {return errorCode_;} + /** \return error data for last error. */ + int errorData() const {return status_;} + /** + * Initialize an SD flash memory card with default clock rate and chip + * select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). + * + * \return true for success or false for failure. + */ + bool init(uint8_t sckRateID = SPI_FULL_SPEED, + uint8_t chipSelectPin = SD_CHIP_SELECT_PIN); + bool readBlock(uint32_t block, uint8_t* dst); + /** + * Read a card's CID register. The CID contains card identification + * information such as Manufacturer ID, Product name, Product serial + * number and Manufacturing date. + * + * \param[out] cid pointer to area for returned data. + * + * \return true for success or false for failure. + */ + bool readCID(cid_t* cid) { + return readRegister(CMD10, cid); + } + /** + * Read a card's CSD register. The CSD contains Card-Specific Data that + * provides information regarding access to the card's contents. + * + * \param[out] csd pointer to area for returned data. + * + * \return true for success or false for failure. + */ + bool readCSD(csd_t* csd) { + return readRegister(CMD9, csd); + } + bool readData(uint8_t *dst); + bool readStart(uint32_t blockNumber); + bool readStop(); + bool setSckRate(uint8_t sckRateID); + /** Return the card type: SD V1, SD V2 or SDHC + * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. + */ + int type() const {return type_;} + bool writeBlock(uint32_t blockNumber, const uint8_t* src); + bool writeData(const uint8_t* src); + bool writeStart(uint32_t blockNumber, uint32_t eraseCount); + bool writeStop(); + + private: + //---------------------------------------------------------------------------- + uint8_t chipSelectPin_; + uint8_t errorCode_; + uint8_t spiRate_; + uint8_t status_; + uint8_t type_; + // private functions + uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { + cardCommand(CMD55, 0); + return cardCommand(cmd, arg); + } + uint8_t cardCommand(uint8_t cmd, uint32_t arg); + bool readData(uint8_t* dst, uint16_t count); + bool readRegister(uint8_t cmd, void* buf); + void chipSelectHigh(); + void chipSelectLow(); + void type(uint8_t value) {type_ = value;} + bool waitNotBusy(uint16_t timeoutMillis); + bool writeData(uint8_t token, const uint8_t* src); +}; + +/** + * \file + * \brief FAT file structures + */ +/* + * mostly from Microsoft document fatgen103.doc + * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + */ +//------------------------------------------------------------------------------ +/** Value for byte 510 of boot block or MBR */ +uint8_t const BOOTSIG0 = 0X55; +/** Value for byte 511 of boot block or MBR */ +uint8_t const BOOTSIG1 = 0XAA; +/** Value for bootSignature field int FAT/FAT32 boot sector */ +uint8_t const EXTENDED_BOOT_SIG = 0X29; +//------------------------------------------------------------------------------ +/** + * \struct partitionTable + * \brief MBR partition table entry + * + * A partition table entry for a MBR formatted storage device. + * The MBR partition table has four entries. + */ +struct partitionTable { + /** + * Boot Indicator . Indicates whether the volume is the active + * partition. Legal values include: 0X00. Do not use for booting. + * 0X80 Active partition. + */ + uint8_t boot; + /** + * Head part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t beginHead; + /** + * Sector part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned beginSector : 6; + /** High bits cylinder for first block in partition. */ + unsigned beginCylinderHigh : 2; + /** + * Combine beginCylinderLow with beginCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t beginCylinderLow; + /** + * Partition type. See defines that begin with PART_TYPE_ for + * some Microsoft partition types. + */ + uint8_t type; + /** + * head part of cylinder-head-sector address of the last sector in the + * partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t endHead; + /** + * Sector part of cylinder-head-sector address of the last sector in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned endSector : 6; + /** High bits of end cylinder */ + unsigned endCylinderHigh : 2; + /** + * Combine endCylinderLow with endCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t endCylinderLow; + /** Logical block address of the first block in the partition. */ + uint32_t firstSector; + /** Length of the partition, in blocks. */ + uint32_t totalSectors; +}; +/** Type name for partitionTable */ +typedef struct partitionTable part_t; +//------------------------------------------------------------------------------ +/** + * \struct masterBootRecord + * + * \brief Master Boot Record + * + * The first block of a storage device that is formatted with a MBR. + */ +struct masterBootRecord { + /** Code Area for master boot program. */ + uint8_t codeArea[440]; + /** Optional Windows NT disk signature. May contain boot code. */ + uint32_t diskSignature; + /** Usually zero but may be more boot code. */ + uint16_t usuallyZero; + /** Partition tables. */ + part_t part[4]; + /** First MBR signature byte. Must be 0X55 */ + uint8_t mbrSig0; + /** Second MBR signature byte. Must be 0XAA */ + uint8_t mbrSig1; +}; +/** Type name for masterBootRecord */ +typedef struct masterBootRecord mbr_t; +//------------------------------------------------------------------------------ +/** + * \struct fat_boot + * + * \brief Boot sector for a FAT12/FAT16 volume. + * + */ +struct fat_boot { + /** + * The first three bytes of the boot sector must be valid, + * executable x 86-based CPU instructions. This includes a + * jump instruction that skips the next nonexecutable bytes. + */ + uint8_t jump[3]; + /** + * This is typically a string of characters that identifies + * the operating system that formatted the volume. + */ + char oemId[8]; + /** + * The size of a hardware sector. Valid decimal values for this + * field are 512, 1024, 2048, and 4096. For most disks used in + * the United States, the value of this field is 512. + */ + uint16_t bytesPerSector; + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. + */ + uint8_t sectorsPerCluster; + /** + * The number of sectors preceding the start of the first FAT, + * including the boot sector. The value of this field is always 1. + */ + uint16_t reservedSectorCount; + /** + * The number of copies of the FAT on the volume. + * The value of this field is always 2. + */ + uint8_t fatCount; + /** + * For FAT12 and FAT16 volumes, this field contains the count of + * 32-byte directory entries in the root directory. For FAT32 volumes, + * this field must be set to 0. For FAT12 and FAT16 volumes, this + * value should always specify a count that when multiplied by 32 + * results in a multiple of bytesPerSector. FAT16 volumes should + * use the value 512. + */ + uint16_t rootDirEntryCount; + /** + * This field is the old 16-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then totalSectors32 + * must be nonzero. For FAT32 volumes, this field must be 0. For + * FAT12 and FAT16 volumes, this field contains the sector count, and + * totalSectors32 is 0 if the total sector count fits + * (is less than 0x10000). + */ + uint16_t totalSectors16; + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (nonremovable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ + uint8_t mediaType; + /** + * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrack; + /** Number of heads for interrupt 0x13. Not used otherwise. */ + uint16_t headCount; + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ + uint32_t hidddenSectors; + /** + * This field is the new 32-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then + * totalSectors16 must be nonzero. + */ + uint32_t totalSectors32; + /** + * Related to the BIOS physical drive number. Floppy drives are + * identified as 0x00 and physical hard disks are identified as + * 0x80, regardless of the number of physical disk drives. + * Typically, this value is set prior to issuing an INT 13h BIOS + * call to specify the device to access. The value is only + * relevant if the device is a boot device. + */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 0X29 if next three fields are valid */ + uint8_t bootSignature; + /** + * A random serial number created when formatting a disk, + * which helps to distinguish between disks. + * Usually generated by combining date and time. + */ + uint32_t volumeSerialNumber; + /** + * A field once used to store the volume label. The volume label + * is now stored as a special file in the root directory. + */ + char volumeLabel[11]; + /** + * A field with a value of either FAT, FAT12 or FAT16, + * depending on the disk format. + */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[448]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +}; +/** Type name for FAT Boot Sector */ +typedef struct fat_boot fat_boot_t; +//------------------------------------------------------------------------------ +/** + * \struct fat32_boot + * + * \brief Boot sector for a FAT32 volume. + * + */ +struct fat32_boot { + /** + * The first three bytes of the boot sector must be valid, + * executable x 86-based CPU instructions. This includes a + * jump instruction that skips the next nonexecutable bytes. + */ + uint8_t jump[3]; + /** + * This is typically a string of characters that identifies + * the operating system that formatted the volume. + */ + char oemId[8]; + /** + * The size of a hardware sector. Valid decimal values for this + * field are 512, 1024, 2048, and 4096. For most disks used in + * the United States, the value of this field is 512. + */ + uint16_t bytesPerSector; + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. + */ + uint8_t sectorsPerCluster; + /** + * The number of sectors preceding the start of the first FAT, + * including the boot sector. Must not be zero + */ + uint16_t reservedSectorCount; + /** + * The number of copies of the FAT on the volume. + * The value of this field is always 2. + */ + uint8_t fatCount; + /** + * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0. + */ + uint16_t rootDirEntryCount; + /** + * For FAT32 volumes, this field must be 0. + */ + uint16_t totalSectors16; + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (nonremovable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ + uint8_t mediaType; + /** + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrack; + /** Number of heads for interrupt 0x13. Not used otherwise. */ + uint16_t headCount; + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ + uint32_t hidddenSectors; + /** + * Contains the total number of sectors in the FAT32 volume. + */ + uint32_t totalSectors32; + /** + * Count of sectors occupied by one FAT on FAT32 volumes. + */ + uint32_t sectorsPerFat32; + /** + * This field is only defined for FAT32 media and does not exist on + * FAT12 and FAT16 media. + * Bits 0-3 -- Zero-based number of active FAT. + * Only valid if mirroring is disabled. + * Bits 4-6 -- Reserved. + * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. + * -- 1 means only one FAT is active; it is the one referenced + * in bits 0-3. + * Bits 8-15 -- Reserved. + */ + uint16_t fat32Flags; + /** + * FAT32 version. High byte is major revision number. + * Low byte is minor revision number. Only 0.0 define. + */ + uint16_t fat32Version; + /** + * Cluster number of the first cluster of the root directory for FAT32. + * This usually 2 but not required to be 2. + */ + uint32_t fat32RootCluster; + /** + * Sector number of FSINFO structure in the reserved area of the + * FAT32 volume. Usually 1. + */ + uint16_t fat32FSInfo; + /** + * If nonzero, indicates the sector number in the reserved area + * of the volume of a copy of the boot record. Usually 6. + * No value other than 6 is recommended. + */ + uint16_t fat32BackBootBlock; + /** + * Reserved for future expansion. Code that formats FAT32 volumes + * should always set all of the bytes of this field to 0. + */ + uint8_t fat32Reserved[12]; + /** + * Related to the BIOS physical drive number. Floppy drives are + * identified as 0x00 and physical hard disks are identified as + * 0x80, regardless of the number of physical disk drives. + * Typically, this value is set prior to issuing an INT 13h BIOS + * call to specify the device to access. The value is only + * relevant if the device is a boot device. + */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 0X29 if next three fields are valid */ + uint8_t bootSignature; + /** + * A random serial number created when formatting a disk, + * which helps to distinguish between disks. + * Usually generated by combining date and time. + */ + uint32_t volumeSerialNumber; + /** + * A field once used to store the volume label. The volume label + * is now stored as a special file in the root directory. + */ + char volumeLabel[11]; + /** + * A text field with a value of FAT32. + */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[420]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +}; +/** Type name for FAT32 Boot Sector */ +typedef struct fat32_boot fat32_boot_t; +//------------------------------------------------------------------------------ +/** Lead signature for a FSINFO sector */ +uint32_t const FSINFO_LEAD_SIG = 0x41615252; +/** Struct signature for a FSINFO sector */ +uint32_t const FSINFO_STRUCT_SIG = 0x61417272; +/** + * \struct fat32_fsinfo + * + * \brief FSINFO sector for a FAT32 volume. + * + */ +struct fat32_fsinfo { + /** must be 0X52, 0X52, 0X61, 0X41 */ + uint32_t leadSignature; + /** must be zero */ + uint8_t reserved1[480]; + /** must be 0X72, 0X72, 0X41, 0X61 */ + uint32_t structSignature; + /** + * Contains the last known free cluster count on the volume. + * If the value is 0xFFFFFFFF, then the free count is unknown + * and must be computed. Any other value can be used, but is + * not necessarily correct. It should be range checked at least + * to make sure it is <= volume cluster count. + */ + uint32_t freeCount; + /** + * This is a hint for the FAT driver. It indicates the cluster + * number at which the driver should start looking for free clusters. + * If the value is 0xFFFFFFFF, then there is no hint and the driver + * should start looking at cluster 2. + */ + uint32_t nextFree; + /** must be zero */ + uint8_t reserved2[12]; + /** must be 0X00, 0X00, 0X55, 0XAA */ + uint8_t tailSignature[4]; +}; +/** Type name for FAT32 FSINFO Sector */ +typedef struct fat32_fsinfo fat32_fsinfo_t; +//------------------------------------------------------------------------------ +// End Of Chain values for FAT entries +/** FAT12 end of chain value used by Microsoft. */ +uint16_t const FAT12EOC = 0XFFF; +/** Minimum value for FAT12 EOC. Use to test for EOC. */ +uint16_t const FAT12EOC_MIN = 0XFF8; +/** FAT16 end of chain value used by Microsoft. */ +uint16_t const FAT16EOC = 0XFFFF; +/** Minimum value for FAT16 EOC. Use to test for EOC. */ +uint16_t const FAT16EOC_MIN = 0XFFF8; +/** FAT32 end of chain value used by Microsoft. */ +uint32_t const FAT32EOC = 0X0FFFFFFF; +/** Minimum value for FAT32 EOC. Use to test for EOC. */ +uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; +/** Mask a for FAT32 entry. Entries are 28 bits. */ +uint32_t const FAT32MASK = 0X0FFFFFFF; +//------------------------------------------------------------------------------ +/** + * \struct directoryEntry + * \brief FAT short directory entry + * + * Short means short 8.3 name, not the entry size. + * + * Date Format. A FAT directory entry date stamp is a 16-bit field that is + * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the + * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the + * 16-bit word): + * + * Bits 9-15: Count of years from 1980, valid value range 0-127 + * inclusive (1980-2107). + * + * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. + * + * Bits 0-4: Day of month, valid value range 1-31 inclusive. + * + * Time Format. A FAT directory entry time stamp is a 16-bit field that has + * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the + * 16-bit word, bit 15 is the MSB of the 16-bit word). + * + * Bits 11-15: Hours, valid value range 0-23 inclusive. + * + * Bits 5-10: Minutes, valid value range 0-59 inclusive. + * + * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). + * + * The valid time range is from Midnight 00:00:00 to 23:59:58. + */ +struct directoryEntry { + /** Short 8.3 name. + * + * The first eight bytes contain the file name with blank fill. + * The last three bytes contain the file extension with blank fill. + */ + uint8_t name[11]; + /** Entry attributes. + * + * The upper two bits of the attribute byte are reserved and should + * always be set to 0 when a file is created and never modified or + * looked at after that. See defines that begin with DIR_ATT_. + */ + uint8_t attributes; + /** + * Reserved for use by Windows NT. Set value to 0 when a file is + * created and never modify or look at it after that. + */ + uint8_t reservedNT; + /** + * The granularity of the seconds part of creationTime is 2 seconds + * so this field is a count of tenths of a second and its valid + * value range is 0-199 inclusive. (WHG note - seems to be hundredths) + */ + uint8_t creationTimeTenths; + /** Time file was created. */ + uint16_t creationTime; + /** Date file was created. */ + uint16_t creationDate; + /** + * Last access date. Note that there is no last access time, only + * a date. This is the date of last read or write. In the case of + * a write, this should be set to the same date as lastWriteDate. + */ + uint16_t lastAccessDate; + /** + * High word of this entry's first cluster number (always 0 for a + * FAT12 or FAT16 volume). + */ + uint16_t firstClusterHigh; + /** Time of last write. File creation is considered a write. */ + uint16_t lastWriteTime; + /** Date of last write. File creation is considered a write. */ + uint16_t lastWriteDate; + /** Low word of this entry's first cluster number. */ + uint16_t firstClusterLow; + /** 32-bit unsigned holding this file's size in bytes. */ + uint32_t fileSize; +}; +//------------------------------------------------------------------------------ +// Definitions for directory entries +// +/** Type name for directoryEntry */ +typedef struct directoryEntry dir_t; +/** escape for name[0] = 0XE5 */ +uint8_t const DIR_NAME_0XE5 = 0X05; +/** name[0] value for entry that is free after being "deleted" */ +uint8_t const DIR_NAME_DELETED = 0XE5; +/** name[0] value for entry that is free and no allocated entries follow */ +uint8_t const DIR_NAME_FREE = 0X00; +/** file is read-only */ +uint8_t const DIR_ATT_READ_ONLY = 0X01; +/** File should hidden in directory listings */ +uint8_t const DIR_ATT_HIDDEN = 0X02; +/** Entry is for a system file */ +uint8_t const DIR_ATT_SYSTEM = 0X04; +/** Directory entry contains the volume label */ +uint8_t const DIR_ATT_VOLUME_ID = 0X08; +/** Entry is for a directory */ +uint8_t const DIR_ATT_DIRECTORY = 0X10; +/** Old DOS archive bit for backup support */ +uint8_t const DIR_ATT_ARCHIVE = 0X20; +/** Test value for long name entry. Test is + (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ +uint8_t const DIR_ATT_LONG_NAME = 0X0F; +/** Test mask for long name entry */ +uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; +/** defined attribute bits */ +uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; +/** Directory entry is part of a long name + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for part of a long name else false. + */ +static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { + return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; +} +/** Mask for file/subdirectory tests */ +uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); +/** Directory entry is for a file + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for a normal file else false. + */ +static inline uint8_t DIR_IS_FILE(const dir_t* dir) { + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; +} +/** Directory entry is for a subdirectory + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for a subdirectory else false. + */ +static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; +} +/** Directory entry is for a file or subdirectory + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is for a normal file or subdirectory else false. + */ +static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { + return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; +} + +//============================================================================== +// SdVolume class +/** + * \brief Cache for an SD data block + */ +union cache_t { + /** Used to access cached file data blocks. */ + uint8_t data[512]; + /** Used to access cached FAT16 entries. */ + uint16_t fat16[256]; + /** Used to access cached FAT32 entries. */ + uint32_t fat32[128]; + /** Used to access cached directory entries. */ + dir_t dir[16]; + /** Used to access a cached Master Boot Record. */ + mbr_t mbr; + /** Used to access to a cached FAT boot sector. */ + fat_boot_t fbs; + /** Used to access to a cached FAT32 boot sector. */ + fat32_boot_t fbs32; + /** Used to access to a cached FAT32 FSINFO sector. */ + fat32_fsinfo_t fsinfo; +}; +//------------------------------------------------------------------------------ +/** + * \class SdVolume + * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. + */ +class SdVolume { + public: + /** Create an instance of SdVolume */ + SdVolume() : fatType_(0) {} + /** Clear the cache and returns a pointer to the cache. Used by the WaveRP + * recorder to do raw write to the SD card. Not for normal apps. + * \return A pointer to the cache buffer or zero if an error occurs. + */ + cache_t* cacheClear() { + if (!cacheFlush()) return 0; + cacheBlockNumber_ = 0XFFFFFFFF; + return &cacheBuffer_; + } + /** Initialize a FAT volume. Try partition one first then try super + * floppy format. + * + * \param[in] dev The Sd2Card where the volume is located. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. Reasons for + * failure include not finding a valid partition, not finding a valid + * FAT file system or an I/O error. + */ + bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);} + bool init(Sd2Card* dev, uint8_t part); + + // inline functions that return volume info + /** \return The volume's cluster size in blocks. */ + uint8_t blocksPerCluster() const {return blocksPerCluster_;} + /** \return The number of blocks in one FAT. */ + uint32_t blocksPerFat() const {return blocksPerFat_;} + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount() const {return clusterCount_;} + /** \return The shift count required to multiply by blocksPerCluster. */ + uint8_t clusterSizeShift() const {return clusterSizeShift_;} + /** \return The logical block number for the start of file data. */ + uint32_t dataStartBlock() const {return dataStartBlock_;} + /** \return The number of FAT structures on the volume. */ + uint8_t fatCount() const {return fatCount_;} + /** \return The logical block number for the start of the first FAT. */ + uint32_t fatStartBlock() const {return fatStartBlock_;} + /** \return The FAT type of the volume. Values are 12, 16 or 32. */ + uint8_t fatType() const {return fatType_;} + int32_t freeClusterCount(); + /** \return The number of entries in the root directory for FAT16 volumes. */ + uint32_t rootDirEntryCount() const {return rootDirEntryCount_;} + /** \return The logical block number for the start of the root directory + on FAT16 volumes or the first cluster number on FAT32 volumes. */ + uint32_t rootDirStart() const {return rootDirStart_;} + /** Sd2Card object for this volume + * \return pointer to Sd2Card object. + */ + Sd2Card* sdCard() {return sdCard_;} + /** Debug access to FAT table + * + * \param[in] n cluster number. + * \param[out] v value of entry + * \return true for success or false for failure + */ + bool dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);} +//------------------------------------------------------------------------------ + private: + // Allow SdBaseFile access to SdVolume private data. + friend class SdBaseFile; + + // value for dirty argument in cacheRawBlock to indicate read from cache + static bool const CACHE_FOR_READ = false; + // value for dirty argument in cacheRawBlock to indicate write to cache + static bool const CACHE_FOR_WRITE = true; + +#if USE_MULTIPLE_CARDS + cache_t cacheBuffer_; // 512 byte cache for device blocks + uint32_t cacheBlockNumber_; // Logical number of block in the cache + Sd2Card* sdCard_; // Sd2Card object for cache + bool cacheDirty_; // cacheFlush() will write block if true + uint32_t cacheMirrorBlock_; // block number for mirror FAT +#else // USE_MULTIPLE_CARDS + static cache_t cacheBuffer_; // 512 byte cache for device blocks + static uint32_t cacheBlockNumber_; // Logical number of block in the cache + static Sd2Card* sdCard_; // Sd2Card object for cache + static bool cacheDirty_; // cacheFlush() will write block if true + static uint32_t cacheMirrorBlock_; // block number for mirror FAT +#endif // USE_MULTIPLE_CARDS + uint32_t allocSearchStart_; // start cluster for alloc search + uint8_t blocksPerCluster_; // cluster size in blocks + uint32_t blocksPerFat_; // FAT size in blocks + uint32_t clusterCount_; // clusters in one FAT + uint8_t clusterSizeShift_; // shift to convert cluster count to block count + uint32_t dataStartBlock_; // first data block number + uint8_t fatCount_; // number of FATs on volume + uint32_t fatStartBlock_; // start block for first FAT + uint8_t fatType_; // volume type (12, 16, OR 32) + uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir + uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 + //---------------------------------------------------------------------------- + bool allocContiguous(uint32_t count, uint32_t* curCluster); + uint8_t blockOfCluster(uint32_t position) const { + return (position >> 9) & (blocksPerCluster_ - 1);} + uint32_t clusterStartBlock(uint32_t cluster) const { + return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);} + cache_t *cache() {return &cacheBuffer_;} + uint32_t cacheBlockNumber() {return cacheBlockNumber_;} +#if USE_MULTIPLE_CARDS + bool cacheFlush(); + bool cacheRawBlock(uint32_t blockNumber, bool dirty); +#else // USE_MULTIPLE_CARDS + static bool cacheFlush(); + static bool cacheRawBlock(uint32_t blockNumber, bool dirty); +#endif // USE_MULTIPLE_CARDS + // used by SdBaseFile write to assign cache to SD location + void cacheSetBlockNumber(uint32_t blockNumber, bool dirty) { + cacheDirty_ = dirty; + cacheBlockNumber_ = blockNumber; + } + void cacheSetDirty() {cacheDirty_ |= CACHE_FOR_WRITE;} + bool chainSize(uint32_t beginCluster, uint32_t* size); + bool fatGet(uint32_t cluster, uint32_t* value); + bool fatPut(uint32_t cluster, uint32_t value); + bool fatPutEOC(uint32_t cluster) { + return fatPut(cluster, 0x0FFFFFFF); + } + bool freeChain(uint32_t cluster); + bool isEOC(uint32_t cluster) const { + if (FAT12_SUPPORT && fatType_ == 12) return cluster >= FAT12EOC_MIN; + if (fatType_ == 16) return cluster >= FAT16EOC_MIN; + return cluster >= FAT32EOC_MIN; + } + bool readBlock(uint32_t block, uint8_t* dst) { + return sdCard_->readBlock(block, dst);} + bool writeBlock(uint32_t block, const uint8_t* dst) { + return sdCard_->writeBlock(block, dst); + } +//------------------------------------------------------------------------------ + // Deprecated functions - suppress cpplint warnings with NOLINT comment +#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) + + public: + /** \deprecated Use: bool SdVolume::init(Sd2Card* dev); + * \param[in] dev The SD card where the volume is located. + * \return true for success or false for failure. + */ + bool init(Sd2Card& dev) {return init(&dev);} // NOLINT + /** \deprecated Use: bool SdVolume::init(Sd2Card* dev, uint8_t vol); + * \param[in] dev The SD card where the volume is located. + * \param[in] part The partition to be used. + * \return true for success or false for failure. + */ + bool init(Sd2Card& dev, uint8_t part) { // NOLINT + return init(&dev, part); + } +#endif // ALLOW_DEPRECATED_FUNCTIONS +}; + +//------------------------------------------------------------------------------ +/** + * \struct fpos_t + * \brief internal type for istream + * do not use in user apps + */ +struct fpos_t { + /** stream position */ + uint32_t position; + /** cluster for position */ + uint32_t cluster; + fpos_t() : position(0), cluster(0) {} +}; + +// use the gnu style oflag in open() +/** open() oflag for reading */ +uint8_t const O_READ = 0X01; +/** open() oflag - same as O_IN */ +uint8_t const O_RDONLY = O_READ; +/** open() oflag for write */ +uint8_t const O_WRITE = 0X02; +/** open() oflag - same as O_WRITE */ +uint8_t const O_WRONLY = O_WRITE; +/** open() oflag for reading and writing */ +uint8_t const O_RDWR = (O_READ | O_WRITE); +/** open() oflag mask for access modes */ +uint8_t const O_ACCMODE = (O_READ | O_WRITE); +/** The file offset shall be set to the end of the file prior to each write. */ +uint8_t const O_APPEND = 0X04; +/** synchronous writes - call sync() after each write */ +uint8_t const O_SYNC = 0X08; +/** truncate the file to zero length */ +uint8_t const O_TRUNC = 0X10; +/** set the initial position at the end of the file */ +uint8_t const O_AT_END = 0X20; +/** create the file if nonexistent */ +uint8_t const O_CREAT = 0X40; +/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ +uint8_t const O_EXCL = 0X80; + +// SdBaseFile class static and const definitions +// flags for ls() +/** ls() flag to print modify date */ +uint8_t const LS_DATE = 1; +/** ls() flag to print file size */ +uint8_t const LS_SIZE = 2; +/** ls() flag for recursive list of subdirectories */ +uint8_t const LS_R = 4; + + +// flags for timestamp +/** set the file's last access date */ +uint8_t const T_ACCESS = 1; +/** set the file's creation date and time */ +uint8_t const T_CREATE = 2; +/** Set the file's write date and time */ +uint8_t const T_WRITE = 4; +// values for type_ +/** This file has not been opened. */ +uint8_t const FAT_FILE_TYPE_CLOSED = 0; +/** A normal file */ +uint8_t const FAT_FILE_TYPE_NORMAL = 1; +/** A FAT12 or FAT16 root directory */ +uint8_t const FAT_FILE_TYPE_ROOT_FIXED = 2; +/** A FAT32 root directory */ +uint8_t const FAT_FILE_TYPE_ROOT32 = 3; +/** A subdirectory file*/ +uint8_t const FAT_FILE_TYPE_SUBDIR = 4; +/** Test value for directory type */ +uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; + +/** date field for FAT directory entry + * \param[in] year [1980,2107] + * \param[in] month [1,12] + * \param[in] day [1,31] + * + * \return Packed date for dir_t entry. + */ +static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { + return (year - 1980) << 9 | month << 5 | day; +} +/** year part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted year [1980,2107] + */ +static inline uint16_t FAT_YEAR(uint16_t fatDate) { + return 1980 + (fatDate >> 9); +} +/** month part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted month [1,12] + */ +static inline uint8_t FAT_MONTH(uint16_t fatDate) { + return (fatDate >> 5) & 0XF; +} +/** day part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted day [1,31] + */ +static inline uint8_t FAT_DAY(uint16_t fatDate) { + return fatDate & 0X1F; +} +/** time field for FAT directory entry + * \param[in] hour [0,23] + * \param[in] minute [0,59] + * \param[in] second [0,59] + * + * \return Packed time for dir_t entry. + */ +static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { + return hour << 11 | minute << 5 | second >> 1; +} +/** hour part of FAT directory time field + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted hour [0,23] + */ +static inline uint8_t FAT_HOUR(uint16_t fatTime) { + return fatTime >> 11; +} +/** minute part of FAT directory time field + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted minute [0,59] + */ +static inline uint8_t FAT_MINUTE(uint16_t fatTime) { + return(fatTime >> 5) & 0X3F; +} +/** second part of FAT directory time field + * Note second/2 is stored in packed time. + * + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted second [0,58] + */ +static inline uint8_t FAT_SECOND(uint16_t fatTime) { + return 2*(fatTime & 0X1F); +} +/** Default date for file timestamps is 1 Jan 2000 */ +uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; +/** Default time for file timestamp is 1 am */ +uint16_t const FAT_DEFAULT_TIME = (1 << 11); +//------------------------------------------------------------------------------ +/** + * \class SdBaseFile + * \brief Base class for SdFile with Print and C++ streams. + */ +class SdBaseFile { + public: + /** Create an instance. */ + SdBaseFile() : writeError(false), type_(FAT_FILE_TYPE_CLOSED) {} + SdBaseFile(const char* path, uint8_t oflag); + ~SdBaseFile() {if(isOpen()) close();} + /** + * writeError is set to true if an error occurs during a write(). + * Set writeError to false before calling print() and/or write() and check + * for true after calls to print() and/or write(). + */ + bool writeError; + /** \return value of writeError */ + bool getWriteError() {return writeError;} + /** Set writeError to zero */ + void clearWriteError() {writeError = 0;} + //---------------------------------------------------------------------------- + // helpers for stream classes + /** get position for streams + * \param[out] pos struct to receive position + */ + void getpos(fpos_t* pos); + /** set position for streams + * \param[out] pos struct with value for new position + */ + void setpos(fpos_t* pos); + //---------------------------------------------------------------------------- + bool close(); + bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + bool createContiguous(SdBaseFile* dirFile, + const char* path, uint32_t size); + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster() const {return curCluster_;} + /** \return The current position for a file or directory. */ + uint32_t curPosition() const {return curPosition_;} + /** \return Current working directory */ + static SdBaseFile* cwd() {return cwd_;} + /** Set the date/time callback function + * + * \param[in] dateTime The user's call back function. The callback + * function is of the form: + * + * \code + * void dateTime(uint16_t* date, uint16_t* time) { + * uint16_t year; + * uint8_t month, day, hour, minute, second; + * + * // User gets date and time from GPS or real-time clock here + * + * // return date using FAT_DATE macro to format fields + * *date = FAT_DATE(year, month, day); + * + * // return time using FAT_TIME macro to format fields + * *time = FAT_TIME(hour, minute, second); + * } + * \endcode + * + * Sets the function that is called when a file is created or when + * a file's directory entry is modified by sync(). All timestamps, + * access, creation, and modify, are set when a file is created. + * sync() maintains the last access date and last modify date/time. + * + * See the timestamp() function. + */ + static void dateTimeCallback( + void (*dateTime)(uint16_t* date, uint16_t* time)) { + dateTime_ = dateTime; + } + /** Cancel the date/time callback function. */ + static void dateTimeCallbackCancel() {dateTime_ = 0;} + bool dirEntry(dir_t* dir); + static void dirName(const dir_t& dir, char* name); + bool exists(const char* name); + int16_t fgets(char* str, int16_t num, char* delim = 0); + /** \return The total number of bytes in a file or directory. */ + uint32_t fileSize() const {return fileSize_;} + /** \return The first cluster number for a file or directory. */ + uint32_t firstCluster() const {return firstCluster_;} + bool getFilename(char* name); + /** \return True if this is a directory else false. */ + bool isDir() const {return type_ >= FAT_FILE_TYPE_MIN_DIR;} + /** \return True if this is a normal file else false. */ + bool isFile() const {return type_ == FAT_FILE_TYPE_NORMAL;} + /** \return True if this is an open file/directory else false. */ + bool isOpen() const {return type_ != FAT_FILE_TYPE_CLOSED;} + /** \return True if this is a subdirectory else false. */ + bool isSubDir() const {return type_ == FAT_FILE_TYPE_SUBDIR;} + /** \return True if this is the root directory. */ + bool isRoot() const { + return type_ == FAT_FILE_TYPE_ROOT_FIXED || type_ == FAT_FILE_TYPE_ROOT32; + } + void ls(Print* pr, uint8_t flags = 0, uint8_t indent = 0); + void ls(uint8_t flags = 0); + bool mkdir(SdBaseFile* dir, const char* path, bool pFlag = true); + // alias for backward compactability + bool makeDir(SdBaseFile* dir, const char* path) { + return mkdir(dir, path, false); + } + bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); + bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); + bool open(const char* path, uint8_t oflag = O_READ); + bool openNext(SdBaseFile* dirFile, uint8_t oflag); + bool openRoot(SdVolume* vol); + int peek(); + bool printCreateDateTime(Print* pr); + static void printFatDate(uint16_t fatDate); + static void printFatDate(Print* pr, uint16_t fatDate); + static void printFatTime(uint16_t fatTime); + static void printFatTime(Print* pr, uint16_t fatTime); + bool printModifyDateTime(Print* pr); + bool printName(); + bool printName(Print* pr); + int16_t read(); + int16_t read(void* buf, uint16_t nbyte); + int8_t readDir(dir_t* dir); + static bool remove(SdBaseFile* dirFile, const char* path); + bool remove(); + /** Set the file's current position to zero. */ + void rewind() {seekSet(0);} + bool rename(SdBaseFile* dirFile, const char* newPath); + bool rmdir(); + // for backward compatibility + bool rmDir() {return rmdir();} + bool rmRfStar(); + /** Set the files position to current position + \a pos. See seekSet(). + * \param[in] offset The new position in bytes from the current position. + * \return true for success or false for failure. + */ + bool seekCur(int32_t offset) { + return seekSet(curPosition_ + offset); + } + /** Set the files position to end-of-file + \a offset. See seekSet(). + * \param[in] offset The new position in bytes from end-of-file. + * \return true for success or false for failure. + */ + bool seekEnd(int32_t offset = 0) {return seekSet(fileSize_ + offset);} + bool seekSet(uint32_t pos); + bool sync(); + bool timestamp(SdBaseFile* file); + bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second); + /** Type of file. You should use isFile() or isDir() instead of type() + * if possible. + * + * \return The file or directory type. + */ + uint8_t type() const {return type_;} + bool truncate(uint32_t size); + /** \return SdVolume that contains this file. */ + SdVolume* volume() const {return vol_;} + int16_t write(const void* buf, uint16_t nbyte); +//------------------------------------------------------------------------------ + public: + // allow SdFat to set cwd_ + friend class SdFat; + // global pointer to cwd dir + static SdBaseFile* cwd_; + // data time callback function + static void (*dateTime_)(uint16_t* date, uint16_t* time); + // bits defined in flags_ + // should be 0X0F + static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); + // sync of directory entry required + static uint8_t const F_FILE_DIR_DIRTY = 0X80; + + // private data + uint8_t flags_; // See above for definition of flags_ bits + uint8_t fstate_; // error and eof indicator + uint8_t type_; // type of file see above for values + uint32_t curCluster_; // cluster for current file position + uint32_t curPosition_; // current file position in bytes from beginning + uint32_t dirBlock_; // block for this files directory entry + uint8_t dirIndex_; // index of directory entry in dirBlock + uint32_t fileSize_; // file size in bytes + uint32_t firstCluster_; // first cluster of file + SdVolume* vol_; // volume where file is located + + /** experimental don't use */ + bool openParent(SdBaseFile* dir); + // private functions + bool addCluster(); + bool addDirCluster(); + dir_t* cacheDirEntry(uint8_t action); + int8_t lsPrintNext(Print *pr, uint8_t flags, uint8_t indent); + static bool make83Name(const char* str, uint8_t* name, const char** ptr); + bool mkdir(SdBaseFile* parent, const uint8_t dname[11]); + bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag); + bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags); + dir_t* readDirCache(); +//------------------------------------------------------------------------------ +// to be deleted + static void printDirName(const dir_t& dir, + uint8_t width, bool printSlash); + static void printDirName(Print* pr, const dir_t& dir, + uint8_t width, bool printSlash); +//------------------------------------------------------------------------------ +// Deprecated functions - suppress cpplint warnings with NOLINT comment +#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) + + public: + /** \deprecated Use: + * bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + * \param[out] bgnBlock the first block address for the file. + * \param[out] endBlock the last block address for the file. + * \return true for success or false for failure. + */ + bool contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT + return contiguousRange(&bgnBlock, &endBlock); + } + /** \deprecated Use: + * bool createContiguous(SdBaseFile* dirFile, + * const char* path, uint32_t size) + * \param[in] dirFile The directory where the file will be created. + * \param[in] path A path with a valid DOS 8.3 file name. + * \param[in] size The desired file size. + * \return true for success or false for failure. + */ + bool createContiguous(SdBaseFile& dirFile, // NOLINT + const char* path, uint32_t size) { + return createContiguous(&dirFile, path, size); + } + /** \deprecated Use: + * static void dateTimeCallback( + * void (*dateTime)(uint16_t* date, uint16_t* time)); + * \param[in] dateTime The user's call back function. + */ + static void dateTimeCallback( + void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT + oldDateTime_ = dateTime; + dateTime_ = dateTime ? oldToNew : 0; + } + /** \deprecated Use: bool dirEntry(dir_t* dir); + * \param[out] dir Location for return of the file's directory entry. + * \return true for success or false for failure. + */ + bool dirEntry(dir_t& dir) {return dirEntry(&dir);} // NOLINT + /** \deprecated Use: + * bool mkdir(SdBaseFile* dir, const char* path); + * \param[in] dir An open SdFat instance for the directory that will contain + * the new directory. + * \param[in] path A path with a valid 8.3 DOS name for the new directory. + * \return true for success or false for failure. + */ + bool mkdir(SdBaseFile& dir, const char* path) { // NOLINT + return mkdir(&dir, path); + } + /** \deprecated Use: + * bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); + * \param[in] dirFile An open SdFat instance for the directory containing the + * file to be opened. + * \param[in] path A path with a valid 8.3 DOS name for the file. + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * \return true for success or false for failure. + */ + bool open(SdBaseFile& dirFile, // NOLINT + const char* path, uint8_t oflag) { + return open(&dirFile, path, oflag); + } + /** \deprecated Do not use in new apps + * \param[in] dirFile An open SdFat instance for the directory containing the + * file to be opened. + * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * \return true for success or false for failure. + */ + bool open(SdBaseFile& dirFile, const char* path) { // NOLINT + return open(dirFile, path, O_RDWR); + } + /** \deprecated Use: + * bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); + * \param[in] dirFile An open SdFat instance for the directory. + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + * \return true for success or false for failure. + */ + bool open(SdBaseFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT + return open(&dirFile, index, oflag); + } + /** \deprecated Use: bool openRoot(SdVolume* vol); + * \param[in] vol The FAT volume containing the root directory to be opened. + * \return true for success or false for failure. + */ + bool openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT + /** \deprecated Use: int8_t readDir(dir_t* dir); + * \param[out] dir The dir_t struct that will receive the data. + * \return bytes read for success zero for eof or -1 for failure. + */ + int8_t readDir(dir_t& dir) {return readDir(&dir);} // NOLINT + /** \deprecated Use: + * static uint8_t remove(SdBaseFile* dirFile, const char* path); + * \param[in] dirFile The directory that contains the file. + * \param[in] path The name of the file to be removed. + * \return true for success or false for failure. + */ + static bool remove(SdBaseFile& dirFile, const char* path) { // NOLINT + return remove(&dirFile, path); + } +//------------------------------------------------------------------------------ +// rest are private + private: + static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT + static void oldToNew(uint16_t* date, uint16_t* time) { + uint16_t d; + uint16_t t; + oldDateTime_(d, t); + *date = d; + *time = t; + } +#endif // ALLOW_DEPRECATED_FUNCTIONS +}; +//------------------------------------------------------------------------------ +/** + * \class SdFile + * \brief SdBaseFile with Print. + */ +class SdFile : public SdBaseFile, public Print { + public: + SdFile() {} + SdFile(const char* name, uint8_t oflag); + /** \return value of writeError */ + bool getWriteError() {return SdBaseFile::getWriteError();} + /** Set writeError to zero */ + void clearWriteError() {SdBaseFile::clearWriteError();} +#ifdef COMPAT_PRE1 + void write(uint8_t b); +#else + size_t write(uint8_t b); +#endif + int16_t write(const char* str); + int16_t write(const void* buf, uint16_t nbyte); + void write_P(PGM_P str); + void writeln_P(PGM_P str); +}; +/** Store and print a string in flash memory.*/ +#define PgmPrint(x) SerialPrint_P(PSTR(x)) +/** Store and print a string in flash memory followed by a CR/LF.*/ +#define PgmPrintln(x) SerialPrintln_P(PSTR(x)) + +namespace SdFatUtil { + int FreeRam(); + void print_P(Print* pr, PGM_P str); + void println_P(Print* pr, PGM_P str); + void SerialPrint_P(PGM_P str); + void SerialPrintln_P(PGM_P str); +} + +using namespace SdFatUtil; // NOLINT + +//#include +//#include +//------------------------------------------------------------------------------ +/** + * \class SdFat + * \brief Integration class for the %SdFat library. + */ +class SdFat { + public: + SdFat() {} + /** + * Initialize an SdFat object. Arduino friendly version of init. + * + * Initializes the SD card, SD volume, and root directory. + * + * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init(). + * \param[in] sckRateID value for SPI SCK rate. See Sd2Card::init(). + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ + bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN, + uint8_t sckRateID = SPI_FULL_SPEED) { + return init(sckRateID, chipSelectPin); + } + /** \return a pointer to the Sd2Card object. */ + Sd2Card* card() {return &card_;} + bool chdir(bool set_cwd = false); + bool chdir(const char* path, bool set_cwd = false); + void chvol(); + void errorHalt(); + void errorHalt_P(PGM_P msg); + void errorHalt(char const *msg); + void errorPrint(); + void errorPrint_P(PGM_P msg); + void errorPrint(char const *msg); + bool exists(const char* name); + bool init(uint8_t sckRateID = SPI_FULL_SPEED, + uint8_t chipSelectPin = SD_CHIP_SELECT_PIN); + void initErrorHalt(); + void initErrorHalt(char const *msg); + void initErrorHalt_P(PGM_P msg); + void initErrorPrint(); + void initErrorPrint(char const *msg); + void initErrorPrint_P(PGM_P msg); + void ls(uint8_t flags = 0); + void ls(Print* pr, uint8_t flags = 0); + bool mkdir(const char* path, bool pFlag = true); + bool remove(const char* path); + bool rename(const char *oldPath, const char *newPath); + bool rmdir(const char* path); + bool truncate(const char* path, uint32_t length); + /** \return a pointer to the SdVolume object. */ + SdVolume* vol() {return &vol_;} + /** \return a pointer to the volume working directory. */ + SdBaseFile* vwd() {return &vwd_;} + //---------------------------------------------------------------------------- + // static functions for stdOut + /** + * Set stdOut Print stream for messages. + * \param[in] stream The new Print stream. + */ + static void setStdOut(Print* stream) {stdOut_ = stream;} + /** \return Print stream for messages. */ + static Print* stdOut() {return stdOut_;} + + private: + Sd2Card card_; + SdVolume vol_; + SdBaseFile vwd_; + static Print* stdOut_; +}; +#endif // SdFat_h diff --git a/Repetier/fastio.h b/Repetier/fastio.h new file mode 100644 index 0000000..02fcbf7 --- /dev/null +++ b/Repetier/fastio.h @@ -0,0 +1,3048 @@ +/* + This code contibuted by Triffid_Hunter and modified by Kliment + why double up on these macros? see http://gcc.gnu.org/onlinedocs/cpp/Stringification.html + 2012/3/10 AT90USB128x modified by lincomatic to match Teensyduino +*/ +#ifndef _ARDUINO_H +#define _ARDUINO_H + +#include + +/* + utility functions +*/ + +#ifndef MASK +/// MASKING- returns \f$2^PIN\f$ + #define MASK(PIN) (1 << PIN) +#endif + +/* + magic I/O routines + + now you can simply SET_OUTPUT(STEP); WRITE(STEP, 1); WRITE(STEP, 0); +*/ + +/// Read a pin +#define _READ(IO) ((bool)(DIO ## IO ## _RPORT & MASK(DIO ## IO ## _PIN))) +/// write to a pin +#define _WRITE(IO, v) do { if (v) {DIO ## IO ## _WPORT |= MASK(DIO ## IO ## _PIN); } else {DIO ## IO ## _WPORT &= ~MASK(DIO ## IO ## _PIN); }; } while (0) +/// toggle a pin +#define _TOGGLE(IO) do {DIO ## IO ## _RPORT = MASK(DIO ## IO ## _PIN); } while (0) + +/// set pin as input +#define _SET_INPUT(IO) do {DIO ## IO ## _DDR &= ~MASK(DIO ## IO ## _PIN); } while (0) +/// set pin as output +#define _SET_OUTPUT(IO) do {DIO ## IO ## _DDR |= MASK(DIO ## IO ## _PIN); } while (0) + +/// check if pin is an input +#define _GET_INPUT(IO) ((DIO ## IO ## _DDR & MASK(DIO ## IO ## _PIN)) == 0) +/// check if pin is an output +#define _GET_OUTPUT(IO) ((DIO ## IO ## _DDR & MASK(DIO ## IO ## _PIN)) != 0) + +// why double up on these macros? see http://gcc.gnu.org/onlinedocs/cpp/Stringification.html + +/// Read a pin wrapper +#define READ(IO) _READ(IO) +/// Write to a pin wrapper +#define WRITE(IO, v) _WRITE(IO, v) +/// toggle a pin wrapper +#define TOGGLE(IO) _TOGGLE(IO) + +/// set pin as input wrapper +#define SET_INPUT(IO) _SET_INPUT(IO) +/// set pin as output wrapper +#define SET_OUTPUT(IO) _SET_OUTPUT(IO) + +/// check if pin is an input wrapper +#define GET_INPUT(IO) _GET_INPUT(IO) +/// check if pin is an output wrapper +#define GET_OUTPUT(IO) _GET_OUTPUT(IO) + +/* + ports and functions + + added as necessary or if I feel like it- not a comprehensive list! +*/ + +#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega328__) || defined (__AVR_ATmega328P__) +// UART +#define RXD DIO0 +#define TXD DIO1 + +// SPI +#define SCK DIO13 +#define MISO DIO12 +#define MOSI DIO11 +#define SS DIO10 + +// TWI (I2C) +#define SCL AIO5 +#define SDA AIO4 + +// timers and PWM +#define OC0A DIO6 +#define OC0B DIO5 +#define OC1A DIO9 +#define OC1B DIO10 +#define OC2A DIO11 +#define OC2B DIO3 + +#define DEBUG_LED AIO5 + +/* +pins +*/ + +#define DIO0_PIN PIND0 +#define DIO0_RPORT PIND +#define DIO0_WPORT PORTD +#define DIO0_DDR DDRD +#define DIO0_PWM NULL + +#define DIO1_PIN PIND1 +#define DIO1_RPORT PIND +#define DIO1_WPORT PORTD +#define DIO1_DDR DDRD +#define DIO1_PWM NULL + +#define DIO2_PIN PIND2 +#define DIO2_RPORT PIND +#define DIO2_WPORT PORTD +#define DIO2_DDR DDRD +#define DIO2_PWM NULL + +#define DIO3_PIN PIND3 +#define DIO3_RPORT PIND +#define DIO3_WPORT PORTD +#define DIO3_DDR DDRD +#define DIO3_PWM &OCR2B + +#define DIO4_PIN PIND4 +#define DIO4_RPORT PIND +#define DIO4_WPORT PORTD +#define DIO4_DDR DDRD +#define DIO4_PWM NULL + +#define DIO5_PIN PIND5 +#define DIO5_RPORT PIND +#define DIO5_WPORT PORTD +#define DIO5_DDR DDRD +#define DIO5_PWM &OCR0B + +#define DIO6_PIN PIND6 +#define DIO6_RPORT PIND +#define DIO6_WPORT PORTD +#define DIO6_DDR DDRD +#define DIO6_PWM &OCR0A + +#define DIO7_PIN PIND7 +#define DIO7_RPORT PIND +#define DIO7_WPORT PORTD +#define DIO7_DDR DDRD +#define DIO7_PWM NULL + +#define DIO8_PIN PINB0 +#define DIO8_RPORT PINB +#define DIO8_WPORT PORTB +#define DIO8_DDR DDRB +#define DIO8_PWM NULL + +#define DIO9_PIN PINB1 +#define DIO9_RPORT PINB +#define DIO9_WPORT PORTB +#define DIO9_DDR DDRB +#define DIO9_PWM NULL + +#define DIO10_PIN PINB2 +#define DIO10_RPORT PINB +#define DIO10_WPORT PORTB +#define DIO10_DDR DDRB +#define DIO10_PWM NULL + +#define DIO11_PIN PINB3 +#define DIO11_RPORT PINB +#define DIO11_WPORT PORTB +#define DIO11_DDR DDRB +#define DIO11_PWM &OCR2A + +#define DIO12_PIN PINB4 +#define DIO12_RPORT PINB +#define DIO12_WPORT PORTB +#define DIO12_DDR DDRB +#define DIO12_PWM NULL + +#define DIO13_PIN PINB5 +#define DIO13_RPORT PINB +#define DIO13_WPORT PORTB +#define DIO13_DDR DDRB +#define DIO13_PWM NULL + + +#define DIO14_PIN PINC0 +#define DIO14_RPORT PINC +#define DIO14_WPORT PORTC +#define DIO14_DDR DDRC +#define DIO14_PWM NULL + +#define DIO15_PIN PINC1 +#define DIO15_RPORT PINC +#define DIO15_WPORT PORTC +#define DIO15_DDR DDRC +#define DIO15_PWM NULL + +#define DIO16_PIN PINC2 +#define DIO16_RPORT PINC +#define DIO16_WPORT PORTC +#define DIO16_DDR DDRC +#define DIO16_PWM NULL + +#define DIO17_PIN PINC3 +#define DIO17_RPORT PINC +#define DIO17_WPORT PORTC +#define DIO17_DDR DDRC +#define DIO17_PWM NULL + +#define DIO18_PIN PINC4 +#define DIO18_RPORT PINC +#define DIO18_WPORT PORTC +#define DIO18_DDR DDRC +#define DIO18_PWM NULL + +#define DIO19_PIN PINC5 +#define DIO19_RPORT PINC +#define DIO19_WPORT PORTC +#define DIO19_DDR DDRC +#define DIO19_PWM NULL + +#define DIO20_PIN PINC6 +#define DIO20_RPORT PINC +#define DIO20_WPORT PORTC +#define DIO20_DDR DDRC +#define DIO20_PWM NULL + +#define DIO21_PIN PINC7 +#define DIO21_RPORT PINC +#define DIO21_WPORT PORTC +#define DIO21_DDR DDRC +#define DIO21_PWM NULL + + + +#undef PB0 +#define PB0_PIN PINB0 +#define PB0_RPORT PINB +#define PB0_WPORT PORTB +#define PB0_DDR DDRB +#define PB0_PWM NULL + +#undef PB1 +#define PB1_PIN PINB1 +#define PB1_RPORT PINB +#define PB1_WPORT PORTB +#define PB1_DDR DDRB +#define PB1_PWM NULL + +#undef PB2 +#define PB2_PIN PINB2 +#define PB2_RPORT PINB +#define PB2_WPORT PORTB +#define PB2_DDR DDRB +#define PB2_PWM NULL + +#undef PB3 +#define PB3_PIN PINB3 +#define PB3_RPORT PINB +#define PB3_WPORT PORTB +#define PB3_DDR DDRB +#define PB3_PWM &OCR2A + +#undef PB4 +#define PB4_PIN PINB4 +#define PB4_RPORT PINB +#define PB4_WPORT PORTB +#define PB4_DDR DDRB +#define PB4_PWM NULL + +#undef PB5 +#define PB5_PIN PINB5 +#define PB5_RPORT PINB +#define PB5_WPORT PORTB +#define PB5_DDR DDRB +#define PB5_PWM NULL + +#undef PB6 +#define PB6_PIN PINB6 +#define PB6_RPORT PINB +#define PB6_WPORT PORTB +#define PB6_DDR DDRB +#define PB6_PWM NULL + +#undef PB7 +#define PB7_PIN PINB7 +#define PB7_RPORT PINB +#define PB7_WPORT PORTB +#define PB7_DDR DDRB +#define PB7_PWM NULL + + +#undef PC0 +#define PC0_PIN PINC0 +#define PC0_RPORT PINC +#define PC0_WPORT PORTC +#define PC0_DDR DDRC +#define PC0_PWM NULL + +#undef PC1 +#define PC1_PIN PINC1 +#define PC1_RPORT PINC +#define PC1_WPORT PORTC +#define PC1_DDR DDRC +#define PC1_PWM NULL + +#undef PC2 +#define PC2_PIN PINC2 +#define PC2_RPORT PINC +#define PC2_WPORT PORTC +#define PC2_DDR DDRC +#define PC2_PWM NULL + +#undef PC3 +#define PC3_PIN PINC3 +#define PC3_RPORT PINC +#define PC3_WPORT PORTC +#define PC3_DDR DDRC +#define PC3_PWM NULL + +#undef PC4 +#define PC4_PIN PINC4 +#define PC4_RPORT PINC +#define PC4_WPORT PORTC +#define PC4_DDR DDRC +#define PC4_PWM NULL + +#undef PC5 +#define PC5_PIN PINC5 +#define PC5_RPORT PINC +#define PC5_WPORT PORTC +#define PC5_DDR DDRC +#define PC5_PWM NULL + +#undef PC6 +#define PC6_PIN PINC6 +#define PC6_RPORT PINC +#define PC6_WPORT PORTC +#define PC6_DDR DDRC +#define PC6_PWM NULL + +#undef PC7 +#define PC7_PIN PINC7 +#define PC7_RPORT PINC +#define PC7_WPORT PORTC +#define PC7_DDR DDRC +#define PC7_PWM NULL + + +#undef PD0 +#define PD0_PIN PIND0 +#define PD0_RPORT PIND +#define PD0_WPORT PORTD +#define PD0_DDR DDRD +#define PD0_PWM NULL + +#undef PD1 +#define PD1_PIN PIND1 +#define PD1_RPORT PIND +#define PD1_WPORT PORTD +#define PD1_DDR DDRD +#define PD1_PWM NULL + +#undef PD2 +#define PD2_PIN PIND2 +#define PD2_RPORT PIND +#define PD2_WPORT PORTD +#define PD2_DDR DDRD +#define PD2_PWM NULL + +#undef PD3 +#define PD3_PIN PIND3 +#define PD3_RPORT PIND +#define PD3_WPORT PORTD +#define PD3_DDR DDRD +#define PD3_PWM &OCR2B + +#undef PD4 +#define PD4_PIN PIND4 +#define PD4_RPORT PIND +#define PD4_WPORT PORTD +#define PD4_DDR DDRD +#define PD4_PWM NULL + +#undef PD5 +#define PD5_PIN PIND5 +#define PD5_RPORT PIND +#define PD5_WPORT PORTD +#define PD5_DDR DDRD +#define PD5_PWM &OCR0B + +#undef PD6 +#define PD6_PIN PIND6 +#define PD6_RPORT PIND +#define PD6_WPORT PORTD +#define PD6_DDR DDRD +#define PD6_PWM &OCR0A + +#undef PD7 +#define PD7_PIN PIND7 +#define PD7_RPORT PIND +#define PD7_WPORT PORTD +#define PD7_DDR DDRD +#define PD7_PWM NULL +#endif /* _AVR_ATmega{168,328,328P}__ */ + +#if defined (__AVR_ATmega644__) || defined (__AVR_ATmega644P__) || defined (__AVR_ATmega644PA__) || defined (__AVR_ATmega1284P__) +// UART +#define RXD DIO8 +#define TXD DIO9 +#define RXD0 DIO8 +#define TXD0 DIO9 + +#define RXD1 DIO10 +#define TXD1 DIO11 + +// SPI +#define SCK DIO7 +#define MISO DIO6 +#define MOSI DIO5 +#define SS DIO4 + +// TWI (I2C) +#define SCL DIO16 +#define SDA DIO17 + +// timers and PWM +#define OC0A DIO3 +#define OC0B DIO4 +#define OC1A DIO13 +#define OC1B DIO12 +#define OC2A DIO15 +#define OC2B DIO14 + +#define DEBUG_LED DIO0 +/* +pins +*/ + +#define DIO0_PIN PINB0 +#define DIO0_RPORT PINB +#define DIO0_WPORT PORTB +#define DIO0_DDR DDRB +#define DIO0_PWM NULL + +#define DIO1_PIN PINB1 +#define DIO1_RPORT PINB +#define DIO1_WPORT PORTB +#define DIO1_DDR DDRB +#define DIO1_PWM NULL + +#define DIO2_PIN PINB2 +#define DIO2_RPORT PINB +#define DIO2_WPORT PORTB +#define DIO2_DDR DDRB +#define DIO2_PWM NULL + +#define DIO3_PIN PINB3 +#define DIO3_RPORT PINB +#define DIO3_WPORT PORTB +#define DIO3_DDR DDRB +#define DIO3_PWM &OCR0A + +#define DIO4_PIN PINB4 +#define DIO4_RPORT PINB +#define DIO4_WPORT PORTB +#define DIO4_DDR DDRB +#define DIO4_PWM &OCR0B + +#define DIO5_PIN PINB5 +#define DIO5_RPORT PINB +#define DIO5_WPORT PORTB +#define DIO5_DDR DDRB +#define DIO5_PWM NULL + +#define DIO6_PIN PINB6 +#define DIO6_RPORT PINB +#define DIO6_WPORT PORTB +#define DIO6_DDR DDRB +#define DIO6_PWM NULL + +#define DIO7_PIN PINB7 +#define DIO7_RPORT PINB +#define DIO7_WPORT PORTB +#define DIO7_DDR DDRB +#define DIO7_PWM NULL + +#define DIO8_PIN PIND0 +#define DIO8_RPORT PIND +#define DIO8_WPORT PORTD +#define DIO8_DDR DDRD +#define DIO8_PWM NULL + +#define DIO9_PIN PIND1 +#define DIO9_RPORT PIND +#define DIO9_WPORT PORTD +#define DIO9_DDR DDRD +#define DIO9_PWM NULL + +#define DIO10_PIN PIND2 +#define DIO10_RPORT PIND +#define DIO10_WPORT PORTD +#define DIO10_DDR DDRD +#define DIO10_PWM NULL + +#define DIO11_PIN PIND3 +#define DIO11_RPORT PIND +#define DIO11_WPORT PORTD +#define DIO11_DDR DDRD +#define DIO11_PWM NULL + +#define DIO12_PIN PIND4 +#define DIO12_RPORT PIND +#define DIO12_WPORT PORTD +#define DIO12_DDR DDRD +#define DIO12_PWM NULL + +#define DIO13_PIN PIND5 +#define DIO13_RPORT PIND +#define DIO13_WPORT PORTD +#define DIO13_DDR DDRD +#define DIO13_PWM NULL + +#define DIO14_PIN PIND6 +#define DIO14_RPORT PIND +#define DIO14_WPORT PORTD +#define DIO14_DDR DDRD +#define DIO14_PWM &OCR2B + +#define DIO15_PIN PIND7 +#define DIO15_RPORT PIND +#define DIO15_WPORT PORTD +#define DIO15_DDR DDRD +#define DIO15_PWM &OCR2A + +#define DIO16_PIN PINC0 +#define DIO16_RPORT PINC +#define DIO16_WPORT PORTC +#define DIO16_DDR DDRC +#define DIO16_PWM NULL + +#define DIO17_PIN PINC1 +#define DIO17_RPORT PINC +#define DIO17_WPORT PORTC +#define DIO17_DDR DDRC +#define DIO17_PWM NULL + +#define DIO18_PIN PINC2 +#define DIO18_RPORT PINC +#define DIO18_WPORT PORTC +#define DIO18_DDR DDRC +#define DIO18_PWM NULL + +#define DIO19_PIN PINC3 +#define DIO19_RPORT PINC +#define DIO19_WPORT PORTC +#define DIO19_DDR DDRC +#define DIO19_PWM NULL + +#define DIO20_PIN PINC4 +#define DIO20_RPORT PINC +#define DIO20_WPORT PORTC +#define DIO20_DDR DDRC +#define DIO20_PWM NULL + +#define DIO21_PIN PINC5 +#define DIO21_RPORT PINC +#define DIO21_WPORT PORTC +#define DIO21_DDR DDRC +#define DIO21_PWM NULL + +#define DIO22_PIN PINC6 +#define DIO22_RPORT PINC +#define DIO22_WPORT PORTC +#define DIO22_DDR DDRC +#define DIO22_PWM NULL + +#define DIO23_PIN PINC7 +#define DIO23_RPORT PINC +#define DIO23_WPORT PORTC +#define DIO23_DDR DDRC +#define DIO23_PWM NULL + +#define DIO24_PIN PINA7 +#define DIO24_RPORT PINA +#define DIO24_WPORT PORTA +#define DIO24_DDR DDRA +#define DIO24_PWM NULL + +#define DIO25_PIN PINA6 +#define DIO25_RPORT PINA +#define DIO25_WPORT PORTA +#define DIO25_DDR DDRA +#define DIO25_PWM NULL + +#define DIO26_PIN PINA5 +#define DIO26_RPORT PINA +#define DIO26_WPORT PORTA +#define DIO26_DDR DDRA +#define DIO26_PWM NULL + +#define DIO27_PIN PINA4 +#define DIO27_RPORT PINA +#define DIO27_WPORT PORTA +#define DIO27_DDR DDRA +#define DIO27_PWM NULL + +#define DIO28_PIN PINA3 +#define DIO28_RPORT PINA +#define DIO28_WPORT PORTA +#define DIO28_DDR DDRA +#define DIO28_PWM NULL + +#define DIO29_PIN PINA2 +#define DIO29_RPORT PINA +#define DIO29_WPORT PORTA +#define DIO29_DDR DDRA +#define DIO29_PWM NULL + +#define DIO30_PIN PINA1 +#define DIO30_RPORT PINA +#define DIO30_WPORT PORTA +#define DIO30_DDR DDRA +#define DIO30_PWM NULL + +#define DIO31_PIN PINA0 +#define DIO31_RPORT PINA +#define DIO31_WPORT PORTA +#define DIO31_DDR DDRA +#define DIO31_PWM NULL + +#define AIO0_PIN PINA0 +#define AIO0_RPORT PINA +#define AIO0_WPORT PORTA +#define AIO0_DDR DDRA +#define AIO0_PWM NULL + +#define AIO1_PIN PINA1 +#define AIO1_RPORT PINA +#define AIO1_WPORT PORTA +#define AIO1_DDR DDRA +#define AIO1_PWM NULL + +#define AIO2_PIN PINA2 +#define AIO2_RPORT PINA +#define AIO2_WPORT PORTA +#define AIO2_DDR DDRA +#define AIO2_PWM NULL + +#define AIO3_PIN PINA3 +#define AIO3_RPORT PINA +#define AIO3_WPORT PORTA +#define AIO3_DDR DDRA +#define AIO3_PWM NULL + +#define AIO4_PIN PINA4 +#define AIO4_RPORT PINA +#define AIO4_WPORT PORTA +#define AIO4_DDR DDRA +#define AIO4_PWM NULL + +#define AIO5_PIN PINA5 +#define AIO5_RPORT PINA +#define AIO5_WPORT PORTA +#define AIO5_DDR DDRA +#define AIO5_PWM NULL + +#define AIO6_PIN PINA6 +#define AIO6_RPORT PINA +#define AIO6_WPORT PORTA +#define AIO6_DDR DDRA +#define AIO6_PWM NULL + +#define AIO7_PIN PINA7 +#define AIO7_RPORT PINA +#define AIO7_WPORT PORTA +#define AIO7_DDR DDRA +#define AIO7_PWM NULL + + + +#undef PA0 +#define PA0_PIN PINA0 +#define PA0_RPORT PINA +#define PA0_WPORT PORTA +#define PA0_DDR DDRA +#define PA0_PWM NULL + +#undef PA1 +#define PA1_PIN PINA1 +#define PA1_RPORT PINA +#define PA1_WPORT PORTA +#define PA1_DDR DDRA +#define PA1_PWM NULL + +#undef PA2 +#define PA2_PIN PINA2 +#define PA2_RPORT PINA +#define PA2_WPORT PORTA +#define PA2_DDR DDRA +#define PA2_PWM NULL + +#undef PA3 +#define PA3_PIN PINA3 +#define PA3_RPORT PINA +#define PA3_WPORT PORTA +#define PA3_DDR DDRA +#define PA3_PWM NULL + +#undef PA4 +#define PA4_PIN PINA4 +#define PA4_RPORT PINA +#define PA4_WPORT PORTA +#define PA4_DDR DDRA +#define PA4_PWM NULL + +#undef PA5 +#define PA5_PIN PINA5 +#define PA5_RPORT PINA +#define PA5_WPORT PORTA +#define PA5_DDR DDRA +#define PA5_PWM NULL + +#undef PA6 +#define PA6_PIN PINA6 +#define PA6_RPORT PINA +#define PA6_WPORT PORTA +#define PA6_DDR DDRA +#define PA6_PWM NULL + +#undef PA7 +#define PA7_PIN PINA7 +#define PA7_RPORT PINA +#define PA7_WPORT PORTA +#define PA7_DDR DDRA +#define PA7_PWM NULL + + +#undef PB0 +#define PB0_PIN PINB0 +#define PB0_RPORT PINB +#define PB0_WPORT PORTB +#define PB0_DDR DDRB +#define PB0_PWM NULL + +#undef PB1 +#define PB1_PIN PINB1 +#define PB1_RPORT PINB +#define PB1_WPORT PORTB +#define PB1_DDR DDRB +#define PB1_PWM NULL + +#undef PB2 +#define PB2_PIN PINB2 +#define PB2_RPORT PINB +#define PB2_WPORT PORTB +#define PB2_DDR DDRB +#define PB2_PWM NULL + +#undef PB3 +#define PB3_PIN PINB3 +#define PB3_RPORT PINB +#define PB3_WPORT PORTB +#define PB3_DDR DDRB +#define PB3_PWM &OCR0A + +#undef PB4 +#define PB4_PIN PINB4 +#define PB4_RPORT PINB +#define PB4_WPORT PORTB +#define PB4_DDR DDRB +#define PB4_PWM &OCR0B + +#undef PB5 +#define PB5_PIN PINB5 +#define PB5_RPORT PINB +#define PB5_WPORT PORTB +#define PB5_DDR DDRB +#define PB5_PWM NULL + +#undef PB6 +#define PB6_PIN PINB6 +#define PB6_RPORT PINB +#define PB6_WPORT PORTB +#define PB6_DDR DDRB +#define PB6_PWM NULL + +#undef PB7 +#define PB7_PIN PINB7 +#define PB7_RPORT PINB +#define PB7_WPORT PORTB +#define PB7_DDR DDRB +#define PB7_PWM NULL + + +#undef PC0 +#define PC0_PIN PINC0 +#define PC0_RPORT PINC +#define PC0_WPORT PORTC +#define PC0_DDR DDRC +#define PC0_PWM NULL + +#undef PC1 +#define PC1_PIN PINC1 +#define PC1_RPORT PINC +#define PC1_WPORT PORTC +#define PC1_DDR DDRC +#define PC1_PWM NULL + +#undef PC2 +#define PC2_PIN PINC2 +#define PC2_RPORT PINC +#define PC2_WPORT PORTC +#define PC2_DDR DDRC +#define PC2_PWM NULL + +#undef PC3 +#define PC3_PIN PINC3 +#define PC3_RPORT PINC +#define PC3_WPORT PORTC +#define PC3_DDR DDRC +#define PC3_PWM NULL + +#undef PC4 +#define PC4_PIN PINC4 +#define PC4_RPORT PINC +#define PC4_WPORT PORTC +#define PC4_DDR DDRC +#define PC4_PWM NULL + +#undef PC5 +#define PC5_PIN PINC5 +#define PC5_RPORT PINC +#define PC5_WPORT PORTC +#define PC5_DDR DDRC +#define PC5_PWM NULL + +#undef PC6 +#define PC6_PIN PINC6 +#define PC6_RPORT PINC +#define PC6_WPORT PORTC +#define PC6_DDR DDRC +#define PC6_PWM NULL + +#undef PC7 +#define PC7_PIN PINC7 +#define PC7_RPORT PINC +#define PC7_WPORT PORTC +#define PC7_DDR DDRC +#define PC7_PWM NULL + + +#undef PD0 +#define PD0_PIN PIND0 +#define PD0_RPORT PIND +#define PD0_WPORT PORTD +#define PD0_DDR DDRD +#define PD0_PWM NULL + +#undef PD1 +#define PD1_PIN PIND1 +#define PD1_RPORT PIND +#define PD1_WPORT PORTD +#define PD1_DDR DDRD +#define PD1_PWM NULL + +#undef PD2 +#define PD2_PIN PIND2 +#define PD2_RPORT PIND +#define PD2_WPORT PORTD +#define PD2_DDR DDRD +#define PD2_PWM NULL + +#undef PD3 +#define PD3_PIN PIND3 +#define PD3_RPORT PIND +#define PD3_WPORT PORTD +#define PD3_DDR DDRD +#define PD3_PWM NULL + +#undef PD4 +#define PD4_PIN PIND4 +#define PD4_RPORT PIND +#define PD4_WPORT PORTD +#define PD4_DDR DDRD +#define PD4_PWM NULL + +#undef PD5 +#define PD5_PIN PIND5 +#define PD5_RPORT PIND +#define PD5_WPORT PORTD +#define PD5_DDR DDRD +#define PD5_PWM NULL + +#undef PD6 +#define PD6_PIN PIND6 +#define PD6_RPORT PIND +#define PD6_WPORT PORTD +#define PD6_DDR DDRD +#define PD6_PWM &OCR2B + +#undef PD7 +#define PD7_PIN PIND7 +#define PD7_RPORT PIND +#define PD7_WPORT PORTD +#define PD7_DDR DDRD +#define PD7_PWM &OCR2A +#endif /* _AVR_ATmega{644,644P,644PA}__ */ + +#if defined (__AVR_ATmega1280__) || defined (__AVR_ATmega2560__) +// UART +#define RXD DIO0 +#define TXD DIO1 + +// SPI +#define SCK DIO52 +#define MISO DIO50 +#define MOSI DIO51 +#define SS DIO53 + +// TWI (I2C) +#define SCL DIO21 +#define SDA DIO20 + +// timers and PWM +#define OC0A DIO13 +#define OC0B DIO4 +#define OC1A DIO11 +#define OC1B DIO12 +#define OC2A DIO10 +#define OC2B DIO9 +#define OC3A DIO5 +#define OC3B DIO2 +#define OC3C DIO3 +#define OC4A DIO6 +#define OC4B DIO7 +#define OC4C DIO8 +#define OC5A DIO46 +#define OC5B DIO45 +#define OC5C DIO44 + +// change for your board +#if MOTHERBOARD == 12 +#define DEBUG_LED DIO22 +#else +#define DEBUG_LED DIO21 +#endif +/* +pins +*/ +#define DIO0_PIN PINE0 +#define DIO0_RPORT PINE +#define DIO0_WPORT PORTE +#define DIO0_DDR DDRE +#define DIO0_PWM NULL + +#define DIO1_PIN PINE1 +#define DIO1_RPORT PINE +#define DIO1_WPORT PORTE +#define DIO1_DDR DDRE +#define DIO1_PWM NULL + +#define DIO2_PIN PINE4 +#define DIO2_RPORT PINE +#define DIO2_WPORT PORTE +#define DIO2_DDR DDRE +#define DIO2_PWM &OCR3BL + +#define DIO3_PIN PINE5 +#define DIO3_RPORT PINE +#define DIO3_WPORT PORTE +#define DIO3_DDR DDRE +#define DIO3_PWM &OCR3CL + +#define DIO4_PIN PING5 +#define DIO4_RPORT PING +#define DIO4_WPORT PORTG +#define DIO4_DDR DDRG +#define DIO4_PWM &OCR0B + +#define DIO5_PIN PINE3 +#define DIO5_RPORT PINE +#define DIO5_WPORT PORTE +#define DIO5_DDR DDRE +#define DIO5_PWM &OCR3AL + +#define DIO6_PIN PINH3 +#define DIO6_RPORT PINH +#define DIO6_WPORT PORTH +#define DIO6_DDR DDRH +#define DIO6_PWM &OCR4AL + +#define DIO7_PIN PINH4 +#define DIO7_RPORT PINH +#define DIO7_WPORT PORTH +#define DIO7_DDR DDRH +#define DIO7_PWM &OCR4BL + +#define DIO8_PIN PINH5 +#define DIO8_RPORT PINH +#define DIO8_WPORT PORTH +#define DIO8_DDR DDRH +#define DIO8_PWM &OCR4CL + +#define DIO9_PIN PINH6 +#define DIO9_RPORT PINH +#define DIO9_WPORT PORTH +#define DIO9_DDR DDRH +#define DIO9_PWM &OCR2B + +#define DIO10_PIN PINB4 +#define DIO10_RPORT PINB +#define DIO10_WPORT PORTB +#define DIO10_DDR DDRB +#define DIO10_PWM &OCR2A + +#define DIO11_PIN PINB5 +#define DIO11_RPORT PINB +#define DIO11_WPORT PORTB +#define DIO11_DDR DDRB +#define DIO11_PWM NULL + +#define DIO12_PIN PINB6 +#define DIO12_RPORT PINB +#define DIO12_WPORT PORTB +#define DIO12_DDR DDRB +#define DIO12_PWM NULL + +#define DIO13_PIN PINB7 +#define DIO13_RPORT PINB +#define DIO13_WPORT PORTB +#define DIO13_DDR DDRB +#define DIO13_PWM &OCR0A + +#define DIO14_PIN PINJ1 +#define DIO14_RPORT PINJ +#define DIO14_WPORT PORTJ +#define DIO14_DDR DDRJ +#define DIO14_PWM NULL + +#define DIO15_PIN PINJ0 +#define DIO15_RPORT PINJ +#define DIO15_WPORT PORTJ +#define DIO15_DDR DDRJ +#define DIO15_PWM NULL + +#define DIO16_PIN PINH1 +#define DIO16_RPORT PINH +#define DIO16_WPORT PORTH +#define DIO16_DDR DDRH +#define DIO16_PWM NULL + +#define DIO17_PIN PINH0 +#define DIO17_RPORT PINH +#define DIO17_WPORT PORTH +#define DIO17_DDR DDRH +#define DIO17_PWM NULL + +#define DIO18_PIN PIND3 +#define DIO18_RPORT PIND +#define DIO18_WPORT PORTD +#define DIO18_DDR DDRD +#define DIO18_PWM NULL + +#define DIO19_PIN PIND2 +#define DIO19_RPORT PIND +#define DIO19_WPORT PORTD +#define DIO19_DDR DDRD +#define DIO19_PWM NULL + +#define DIO20_PIN PIND1 +#define DIO20_RPORT PIND +#define DIO20_WPORT PORTD +#define DIO20_DDR DDRD +#define DIO20_PWM NULL + +#define DIO21_PIN PIND0 +#define DIO21_RPORT PIND +#define DIO21_WPORT PORTD +#define DIO21_DDR DDRD +#define DIO21_PWM NULL + +#define DIO22_PIN PINA0 +#define DIO22_RPORT PINA +#define DIO22_WPORT PORTA +#define DIO22_DDR DDRA +#define DIO22_PWM NULL + +#define DIO23_PIN PINA1 +#define DIO23_RPORT PINA +#define DIO23_WPORT PORTA +#define DIO23_DDR DDRA +#define DIO23_PWM NULL + +#define DIO24_PIN PINA2 +#define DIO24_RPORT PINA +#define DIO24_WPORT PORTA +#define DIO24_DDR DDRA +#define DIO24_PWM NULL + +#define DIO25_PIN PINA3 +#define DIO25_RPORT PINA +#define DIO25_WPORT PORTA +#define DIO25_DDR DDRA +#define DIO25_PWM NULL + +#define DIO26_PIN PINA4 +#define DIO26_RPORT PINA +#define DIO26_WPORT PORTA +#define DIO26_DDR DDRA +#define DIO26_PWM NULL + +#define DIO27_PIN PINA5 +#define DIO27_RPORT PINA +#define DIO27_WPORT PORTA +#define DIO27_DDR DDRA +#define DIO27_PWM NULL + +#define DIO28_PIN PINA6 +#define DIO28_RPORT PINA +#define DIO28_WPORT PORTA +#define DIO28_DDR DDRA +#define DIO28_PWM NULL + +#define DIO29_PIN PINA7 +#define DIO29_RPORT PINA +#define DIO29_WPORT PORTA +#define DIO29_DDR DDRA +#define DIO29_PWM NULL + +#define DIO30_PIN PINC7 +#define DIO30_RPORT PINC +#define DIO30_WPORT PORTC +#define DIO30_DDR DDRC +#define DIO30_PWM NULL + +#define DIO31_PIN PINC6 +#define DIO31_RPORT PINC +#define DIO31_WPORT PORTC +#define DIO31_DDR DDRC +#define DIO31_PWM NULL + +#define DIO32_PIN PINC5 +#define DIO32_RPORT PINC +#define DIO32_WPORT PORTC +#define DIO32_DDR DDRC +#define DIO32_PWM NULL + +#define DIO33_PIN PINC4 +#define DIO33_RPORT PINC +#define DIO33_WPORT PORTC +#define DIO33_DDR DDRC +#define DIO33_PWM NULL + +#define DIO34_PIN PINC3 +#define DIO34_RPORT PINC +#define DIO34_WPORT PORTC +#define DIO34_DDR DDRC +#define DIO34_PWM NULL + +#define DIO35_PIN PINC2 +#define DIO35_RPORT PINC +#define DIO35_WPORT PORTC +#define DIO35_DDR DDRC +#define DIO35_PWM NULL + +#define DIO36_PIN PINC1 +#define DIO36_RPORT PINC +#define DIO36_WPORT PORTC +#define DIO36_DDR DDRC +#define DIO36_PWM NULL + +#define DIO37_PIN PINC0 +#define DIO37_RPORT PINC +#define DIO37_WPORT PORTC +#define DIO37_DDR DDRC +#define DIO37_PWM NULL + +#define DIO38_PIN PIND7 +#define DIO38_RPORT PIND +#define DIO38_WPORT PORTD +#define DIO38_DDR DDRD +#define DIO38_PWM NULL + +#define DIO39_PIN PING2 +#define DIO39_RPORT PING +#define DIO39_WPORT PORTG +#define DIO39_DDR DDRG +#define DIO39_PWM NULL + +#define DIO40_PIN PING1 +#define DIO40_RPORT PING +#define DIO40_WPORT PORTG +#define DIO40_DDR DDRG +#define DIO40_PWM NULL + +#define DIO41_PIN PING0 +#define DIO41_RPORT PING +#define DIO41_WPORT PORTG +#define DIO41_DDR DDRG +#define DIO41_PWM NULL + +#define DIO42_PIN PINL7 +#define DIO42_RPORT PINL +#define DIO42_WPORT PORTL +#define DIO42_DDR DDRL +#define DIO42_PWM NULL + +#define DIO43_PIN PINL6 +#define DIO43_RPORT PINL +#define DIO43_WPORT PORTL +#define DIO43_DDR DDRL +#define DIO43_PWM NULL + +#define DIO44_PIN PINL5 +#define DIO44_RPORT PINL +#define DIO44_WPORT PORTL +#define DIO44_DDR DDRL +#define DIO44_PWM &OCR5CL + +#define DIO45_PIN PINL4 +#define DIO45_RPORT PINL +#define DIO45_WPORT PORTL +#define DIO45_DDR DDRL +#define DIO45_PWM &OCR5BL + +#define DIO46_PIN PINL3 +#define DIO46_RPORT PINL +#define DIO46_WPORT PORTL +#define DIO46_DDR DDRL +#define DIO46_PWM &OCR5AL + +#define DIO47_PIN PINL2 +#define DIO47_RPORT PINL +#define DIO47_WPORT PORTL +#define DIO47_DDR DDRL +#define DIO47_PWM NULL + +#define DIO48_PIN PINL1 +#define DIO48_RPORT PINL +#define DIO48_WPORT PORTL +#define DIO48_DDR DDRL +#define DIO48_PWM NULL + +#define DIO49_PIN PINL0 +#define DIO49_RPORT PINL +#define DIO49_WPORT PORTL +#define DIO49_DDR DDRL +#define DIO49_PWM NULL + +#define DIO50_PIN PINB3 +#define DIO50_RPORT PINB +#define DIO50_WPORT PORTB +#define DIO50_DDR DDRB +#define DIO50_PWM NULL + +#define DIO51_PIN PINB2 +#define DIO51_RPORT PINB +#define DIO51_WPORT PORTB +#define DIO51_DDR DDRB +#define DIO51_PWM NULL + +#define DIO52_PIN PINB1 +#define DIO52_RPORT PINB +#define DIO52_WPORT PORTB +#define DIO52_DDR DDRB +#define DIO52_PWM NULL + +#define DIO53_PIN PINB0 +#define DIO53_RPORT PINB +#define DIO53_WPORT PORTB +#define DIO53_DDR DDRB +#define DIO53_PWM NULL + +#define DIO54_PIN PINF0 +#define DIO54_RPORT PINF +#define DIO54_WPORT PORTF +#define DIO54_DDR DDRF +#define DIO54_PWM NULL + +#define DIO55_PIN PINF1 +#define DIO55_RPORT PINF +#define DIO55_WPORT PORTF +#define DIO55_DDR DDRF +#define DIO55_PWM NULL + +#define DIO56_PIN PINF2 +#define DIO56_RPORT PINF +#define DIO56_WPORT PORTF +#define DIO56_DDR DDRF +#define DIO56_PWM NULL + +#define DIO57_PIN PINF3 +#define DIO57_RPORT PINF +#define DIO57_WPORT PORTF +#define DIO57_DDR DDRF +#define DIO57_PWM NULL + +#define DIO58_PIN PINF4 +#define DIO58_RPORT PINF +#define DIO58_WPORT PORTF +#define DIO58_DDR DDRF +#define DIO58_PWM NULL + +#define DIO59_PIN PINF5 +#define DIO59_RPORT PINF +#define DIO59_WPORT PORTF +#define DIO59_DDR DDRF +#define DIO59_PWM NULL + +#define DIO60_PIN PINF6 +#define DIO60_RPORT PINF +#define DIO60_WPORT PORTF +#define DIO60_DDR DDRF +#define DIO60_PWM NULL + +#define DIO61_PIN PINF7 +#define DIO61_RPORT PINF +#define DIO61_WPORT PORTF +#define DIO61_DDR DDRF +#define DIO61_PWM NULL + +#define DIO62_PIN PINK0 +#define DIO62_RPORT PINK +#define DIO62_WPORT PORTK +#define DIO62_DDR DDRK +#define DIO62_PWM NULL + +#define DIO63_PIN PINK1 +#define DIO63_RPORT PINK +#define DIO63_WPORT PORTK +#define DIO63_DDR DDRK +#define DIO63_PWM NULL + +#define DIO64_PIN PINK2 +#define DIO64_RPORT PINK +#define DIO64_WPORT PORTK +#define DIO64_DDR DDRK +#define DIO64_PWM NULL + +#define DIO65_PIN PINK3 +#define DIO65_RPORT PINK +#define DIO65_WPORT PORTK +#define DIO65_DDR DDRK +#define DIO65_PWM NULL + +#define DIO66_PIN PINK4 +#define DIO66_RPORT PINK +#define DIO66_WPORT PORTK +#define DIO66_DDR DDRK +#define DIO66_PWM NULL + +#define DIO67_PIN PINK5 +#define DIO67_RPORT PINK +#define DIO67_WPORT PORTK +#define DIO67_DDR DDRK +#define DIO67_PWM NULL + +#define DIO68_PIN PINK6 +#define DIO68_RPORT PINK +#define DIO68_WPORT PORTK +#define DIO68_DDR DDRK +#define DIO68_PWM NULL + +#define DIO69_PIN PINK7 +#define DIO69_RPORT PINK +#define DIO69_WPORT PORTK +#define DIO69_DDR DDRK +#define DIO69_PWM NULL + +#if MOTHERBOARD == 12 +#define DIO80_PIN PINJ2 +#define DIO80_RPORT PINJ +#define DIO80_WPORT PORTJ +#define DIO80_DDR DDRJ +#define DIO80_PWM NULL + +#define DIO81_PIN PINJ4 +#define DIO81_RPORT PINJ +#define DIO81_WPORT PORTJ +#define DIO81_DDR DDRJ +#define DIO81_PWM NULL + +#define DIO82_PIN PINJ5 +#define DIO82_RPORT PINJ +#define DIO82_WPORT PORTJ +#define DIO82_DDR DDRJ +#define DIO82_PWM NULL + +#define DIO83_PIN PINJ6 +#define DIO83_RPORT PINJ +#define DIO83_WPORT PORTJ +#define DIO83_DDR DDRJ +#define DIO83_PWM NULL + +#define DIO84_PIN PINJ7 +#define DIO84_RPORT PINJ +#define DIO84_WPORT PORTJ +#define DIO84_DDR DDRJ +#define DIO84_PWM NULL + +#define DIO85_PIN PINH7 +#define DIO85_RPORT PINH +#define DIO85_WPORT PORTH +#define DIO85_DDR DDRH +#define DIO85_PWM NULL + +#define DIO86_PIN PINH2 +#define DIO86_RPORT PINH +#define DIO86_WPORT PORTH +#define DIO86_DDR DDRH +#define DIO86_PWM NULL + +#define DIO90_PIN PINE7 +#define DIO90_RPORT PINE +#define DIO90_WPORT PORTE +#define DIO90_DDR DDRE +#define DIO90_PWM NULL + +#define DIO91_PIN PINE2 +#define DIO91_RPORT PINE +#define DIO91_WPORT PORTE +#define DIO91_DDR DDRE +#define DIO91_PWM NULL + +#define DIO92_PIN PIND4 +#define DIO92_RPORT PIND +#define DIO92_WPORT PORTD +#define DIO92_DDR DDRD +#define DIO92_PWM NULL + +#define DIO93_PIN PIND5 +#define DIO93_RPORT PIND +#define DIO93_WPORT PORTD +#define DIO93_DDR DDRD +#define DIO93_PWM NULL + +#define DIO94_PIN PIND6 +#define DIO94_RPORT PIND +#define DIO94_WPORT PORTD +#define DIO94_DDR DDRD +#define DIO94_PWM NULL + +#else + + +#define DIO70_PIN PING4 +#define DIO70_RPORT PING +#define DIO70_WPORT PORTG +#define DIO70_DDR DDRG +#define DIO70_PWM NULL +#define DIO71_PIN PING3 +#define DIO71_RPORT PING +#define DIO71_WPORT PORTG +#define DIO71_DDR DDRG +#define DIO71_PWM NULL +#define DIO72_PIN PINJ2 +#define DIO72_RPORT PINJ +#define DIO72_WPORT PORTJ +#define DIO72_DDR DDRJ +#define DIO72_PWM NULL +#define DIO73_PIN PINJ3 +#define DIO73_RPORT PINJ +#define DIO73_WPORT PORTJ +#define DIO73_DDR DDRJ +#define DIO73_PWM NULL +#define DIO74_PIN PINJ7 +#define DIO74_RPORT PINJ +#define DIO74_WPORT PORTJ +#define DIO74_DDR DDRJ +#define DIO74_PWM NULL +#define DIO75_PIN PINJ4 +#define DIO75_RPORT PINJ +#define DIO75_WPORT PORTJ +#define DIO75_DDR DDRJ +#define DIO75_PWM NULL + + +#define DIO76_PIN PINJ5 +#define DIO76_RPORT PINJ +#define DIO76_WPORT PORTJ +#define DIO76_DDR DDRJ +#define DIO76_PWM NULL +#define DIO77_PIN PINJ6 +#define DIO77_RPORT PINJ +#define DIO77_WPORT PORTJ +#define DIO77_DDR DDRJ +#define DIO77_PWM NULL +#define DIO78_PIN PINE2 +#define DIO78_RPORT PINE +#define DIO78_WPORT PORTE +#define DIO78_DDR DDRE +#define DIO78_PWM NULL +#define DIO79_PIN PINE6 +#define DIO79_RPORT PINE +#define DIO79_WPORT PORTE +#define DIO79_DDR DDRE +#define DIO79_PWM NULL +#define DIO80_PIN PINE7 +#define DIO80_RPORT PINE +#define DIO80_WPORT PORTE +#define DIO80_DDR DDRE +#define DIO80_PWM NULL +#define DIO81_PIN PIND4 +#define DIO81_RPORT PIND +#define DIO81_WPORT PORTD +#define DIO81_DDR DDRD +#define DIO81_PWM NULL + +#endif // MOTHERBOARD == 12 + + +#undef PA0 +#define PA0_PIN PINA0 +#define PA0_RPORT PINA +#define PA0_WPORT PORTA +#define PA0_DDR DDRA +#define PA0_PWM NULL +#undef PA1 +#define PA1_PIN PINA1 +#define PA1_RPORT PINA +#define PA1_WPORT PORTA +#define PA1_DDR DDRA +#define PA1_PWM NULL +#undef PA2 +#define PA2_PIN PINA2 +#define PA2_RPORT PINA +#define PA2_WPORT PORTA +#define PA2_DDR DDRA +#define PA2_PWM NULL +#undef PA3 +#define PA3_PIN PINA3 +#define PA3_RPORT PINA +#define PA3_WPORT PORTA +#define PA3_DDR DDRA +#define PA3_PWM NULL +#undef PA4 +#define PA4_PIN PINA4 +#define PA4_RPORT PINA +#define PA4_WPORT PORTA +#define PA4_DDR DDRA +#define PA4_PWM NULL +#undef PA5 +#define PA5_PIN PINA5 +#define PA5_RPORT PINA +#define PA5_WPORT PORTA +#define PA5_DDR DDRA +#define PA5_PWM NULL +#undef PA6 +#define PA6_PIN PINA6 +#define PA6_RPORT PINA +#define PA6_WPORT PORTA +#define PA6_DDR DDRA +#define PA6_PWM NULL +#undef PA7 +#define PA7_PIN PINA7 +#define PA7_RPORT PINA +#define PA7_WPORT PORTA +#define PA7_DDR DDRA +#define PA7_PWM NULL + +#undef PB0 +#define PB0_PIN PINB0 +#define PB0_RPORT PINB +#define PB0_WPORT PORTB +#define PB0_DDR DDRB +#define PB0_PWM NULL +#undef PB1 +#define PB1_PIN PINB1 +#define PB1_RPORT PINB +#define PB1_WPORT PORTB +#define PB1_DDR DDRB +#define PB1_PWM NULL +#undef PB2 +#define PB2_PIN PINB2 +#define PB2_RPORT PINB +#define PB2_WPORT PORTB +#define PB2_DDR DDRB +#define PB2_PWM NULL +#undef PB3 +#define PB3_PIN PINB3 +#define PB3_RPORT PINB +#define PB3_WPORT PORTB +#define PB3_DDR DDRB +#define PB3_PWM NULL +#undef PB4 +#define PB4_PIN PINB4 +#define PB4_RPORT PINB +#define PB4_WPORT PORTB +#define PB4_DDR DDRB +#define PB4_PWM &OCR2A +#undef PB5 +#define PB5_PIN PINB5 +#define PB5_RPORT PINB +#define PB5_WPORT PORTB +#define PB5_DDR DDRB +#define PB5_PWM NULL +#undef PB6 +#define PB6_PIN PINB6 +#define PB6_RPORT PINB +#define PB6_WPORT PORTB +#define PB6_DDR DDRB +#define PB6_PWM NULL +#undef PB7 +#define PB7_PIN PINB7 +#define PB7_RPORT PINB +#define PB7_WPORT PORTB +#define PB7_DDR DDRB +#define PB7_PWM &OCR0A + +#undef PC0 +#define PC0_PIN PINC0 +#define PC0_RPORT PINC +#define PC0_WPORT PORTC +#define PC0_DDR DDRC +#define PC0_PWM NULL +#undef PC1 +#define PC1_PIN PINC1 +#define PC1_RPORT PINC +#define PC1_WPORT PORTC +#define PC1_DDR DDRC +#define PC1_PWM NULL +#undef PC2 +#define PC2_PIN PINC2 +#define PC2_RPORT PINC +#define PC2_WPORT PORTC +#define PC2_DDR DDRC +#define PC2_PWM NULL +#undef PC3 +#define PC3_PIN PINC3 +#define PC3_RPORT PINC +#define PC3_WPORT PORTC +#define PC3_DDR DDRC +#define PC3_PWM NULL +#undef PC4 +#define PC4_PIN PINC4 +#define PC4_RPORT PINC +#define PC4_WPORT PORTC +#define PC4_DDR DDRC +#define PC4_PWM NULL +#undef PC5 +#define PC5_PIN PINC5 +#define PC5_RPORT PINC +#define PC5_WPORT PORTC +#define PC5_DDR DDRC +#define PC5_PWM NULL +#undef PC6 +#define PC6_PIN PINC6 +#define PC6_RPORT PINC +#define PC6_WPORT PORTC +#define PC6_DDR DDRC +#define PC6_PWM NULL +#undef PC7 +#define PC7_PIN PINC7 +#define PC7_RPORT PINC +#define PC7_WPORT PORTC +#define PC7_DDR DDRC +#define PC7_PWM NULL + +#undef PD0 +#define PD0_PIN PIND0 +#define PD0_RPORT PIND +#define PD0_WPORT PORTD +#define PD0_DDR DDRD +#define PD0_PWM NULL +#undef PD1 +#define PD1_PIN PIND1 +#define PD1_RPORT PIND +#define PD1_WPORT PORTD +#define PD1_DDR DDRD +#define PD1_PWM NULL +#undef PD2 +#define PD2_PIN PIND2 +#define PD2_RPORT PIND +#define PD2_WPORT PORTD +#define PD2_DDR DDRD +#define PD2_PWM NULL +#undef PD3 +#define PD3_PIN PIND3 +#define PD3_RPORT PIND +#define PD3_WPORT PORTD +#define PD3_DDR DDRD +#define PD3_PWM NULL +#undef PD4 +#define PD4_PIN PIND4 +#define PD4_RPORT PIND +#define PD4_WPORT PORTD +#define PD4_DDR DDRD +#define PD4_PWM NULL +#undef PD5 +#define PD5_PIN PIND5 +#define PD5_RPORT PIND +#define PD5_WPORT PORTD +#define PD5_DDR DDRD +#define PD5_PWM NULL +#undef PD6 +#define PD6_PIN PIND6 +#define PD6_RPORT PIND +#define PD6_WPORT PORTD +#define PD6_DDR DDRD +#define PD6_PWM NULL +#undef PD7 +#define PD7_PIN PIND7 +#define PD7_RPORT PIND +#define PD7_WPORT PORTD +#define PD7_DDR DDRD +#define PD7_PWM NULL + +#undef PE0 +#define PE0_PIN PINE0 +#define PE0_RPORT PINE +#define PE0_WPORT PORTE +#define PE0_DDR DDRE +#define PE0_PWM NULL +#undef PE1 +#define PE1_PIN PINE1 +#define PE1_RPORT PINE +#define PE1_WPORT PORTE +#define PE1_DDR DDRE +#define PE1_PWM NULL +#undef PE2 +#define PE2_PIN PINE2 +#define PE2_RPORT PINE +#define PE2_WPORT PORTE +#define PE2_DDR DDRE +#define PE2_PWM NULL +#undef PE3 +#define PE3_PIN PINE3 +#define PE3_RPORT PINE +#define PE3_WPORT PORTE +#define PE3_DDR DDRE +#define PE3_PWM &OCR3AL +#undef PE4 +#define PE4_PIN PINE4 +#define PE4_RPORT PINE +#define PE4_WPORT PORTE +#define PE4_DDR DDRE +#define PE4_PWM &OCR3BL +#undef PE5 +#define PE5_PIN PINE5 +#define PE5_RPORT PINE +#define PE5_WPORT PORTE +#define PE5_DDR DDRE +#define PE5_PWM &OCR3CL +#undef PE6 +#define PE6_PIN PINE6 +#define PE6_RPORT PINE +#define PE6_WPORT PORTE +#define PE6_DDR DDRE +#define PE6_PWM NULL +#undef PE7 +#define PE7_PIN PINE7 +#define PE7_RPORT PINE +#define PE7_WPORT PORTE +#define PE7_DDR DDRE +#define PE7_PWM NULL + +#undef PF0 +#define PF0_PIN PINF0 +#define PF0_RPORT PINF +#define PF0_WPORT PORTF +#define PF0_DDR DDRF +#define PF0_PWM NULL +#undef PF1 +#define PF1_PIN PINF1 +#define PF1_RPORT PINF +#define PF1_WPORT PORTF +#define PF1_DDR DDRF +#define PF1_PWM NULL +#undef PF2 +#define PF2_PIN PINF2 +#define PF2_RPORT PINF +#define PF2_WPORT PORTF +#define PF2_DDR DDRF +#define PF2_PWM NULL +#undef PF3 +#define PF3_PIN PINF3 +#define PF3_RPORT PINF +#define PF3_WPORT PORTF +#define PF3_DDR DDRF +#define PF3_PWM NULL +#undef PF4 +#define PF4_PIN PINF4 +#define PF4_RPORT PINF +#define PF4_WPORT PORTF +#define PF4_DDR DDRF +#define PF4_PWM NULL +#undef PF5 +#define PF5_PIN PINF5 +#define PF5_RPORT PINF +#define PF5_WPORT PORTF +#define PF5_DDR DDRF +#define PF5_PWM NULL +#undef PF6 +#define PF6_PIN PINF6 +#define PF6_RPORT PINF +#define PF6_WPORT PORTF +#define PF6_DDR DDRF +#define PF6_PWM NULL +#undef PF7 +#define PF7_PIN PINF7 +#define PF7_RPORT PINF +#define PF7_WPORT PORTF +#define PF7_DDR DDRF +#define PF7_PWM NULL + +#undef PG0 +#define PG0_PIN PING0 +#define PG0_RPORT PING +#define PG0_WPORT PORTG +#define PG0_DDR DDRG +#define PG0_PWM NULL +#undef PG1 +#define PG1_PIN PING1 +#define PG1_RPORT PING +#define PG1_WPORT PORTG +#define PG1_DDR DDRG +#define PG1_PWM NULL +#undef PG2 +#define PG2_PIN PING2 +#define PG2_RPORT PING +#define PG2_WPORT PORTG +#define PG2_DDR DDRG +#define PG2_PWM NULL +#undef PG3 +#define PG3_PIN PING3 +#define PG3_RPORT PING +#define PG3_WPORT PORTG +#define PG3_DDR DDRG +#define PG3_PWM NULL +#undef PG4 +#define PG4_PIN PING4 +#define PG4_RPORT PING +#define PG4_WPORT PORTG +#define PG4_DDR DDRG +#define PG4_PWM NULL +#undef PG5 +#define PG5_PIN PING5 +#define PG5_RPORT PING +#define PG5_WPORT PORTG +#define PG5_DDR DDRG +#define PG5_PWM &OCR0B +#undef PG6 +#define PG6_PIN PING6 +#define PG6_RPORT PING +#define PG6_WPORT PORTG +#define PG6_DDR DDRG +#define PG6_PWM NULL +#undef PG7 +#define PG7_PIN PING7 +#define PG7_RPORT PING +#define PG7_WPORT PORTG +#define PG7_DDR DDRG +#define PG7_PWM NULL + +#undef PH0 +#define PH0_PIN PINH0 +#define PH0_RPORT PINH +#define PH0_WPORT PORTH +#define PH0_DDR DDRH +#define PH0_PWM NULL +#undef PH1 +#define PH1_PIN PINH1 +#define PH1_RPORT PINH +#define PH1_WPORT PORTH +#define PH1_DDR DDRH +#define PH1_PWM NULL +#undef PH2 +#define PH2_PIN PINH2 +#define PH2_RPORT PINH +#define PH2_WPORT PORTH +#define PH2_DDR DDRH +#define PH2_PWM NULL +#undef PH3 +#define PH3_PIN PINH3 +#define PH3_RPORT PINH +#define PH3_WPORT PORTH +#define PH3_DDR DDRH +#define PH3_PWM &OCR4AL +#undef PH4 +#define PH4_PIN PINH4 +#define PH4_RPORT PINH +#define PH4_WPORT PORTH +#define PH4_DDR DDRH +#define PH4_PWM &OCR4BL +#undef PH5 +#define PH5_PIN PINH5 +#define PH5_RPORT PINH +#define PH5_WPORT PORTH +#define PH5_DDR DDRH +#define PH5_PWM &OCR4CL +#undef PH6 +#define PH6_PIN PINH6 +#define PH6_RPORT PINH +#define PH6_WPORT PORTH +#define PH6_DDR DDRH +#define PH6_PWM &OCR2B +#undef PH7 +#define PH7_PIN PINH7 +#define PH7_RPORT PINH +#define PH7_WPORT PORTH +#define PH7_DDR DDRH +#define PH7_PWM NULL + +#undef PJ0 +#define PJ0_PIN PINJ0 +#define PJ0_RPORT PINJ +#define PJ0_WPORT PORTJ +#define PJ0_DDR DDRJ +#define PJ0_PWM NULL +#undef PJ1 +#define PJ1_PIN PINJ1 +#define PJ1_RPORT PINJ +#define PJ1_WPORT PORTJ +#define PJ1_DDR DDRJ +#define PJ1_PWM NULL +#undef PJ2 +#define PJ2_PIN PINJ2 +#define PJ2_RPORT PINJ +#define PJ2_WPORT PORTJ +#define PJ2_DDR DDRJ +#define PJ2_PWM NULL +#undef PJ3 +#define PJ3_PIN PINJ3 +#define PJ3_RPORT PINJ +#define PJ3_WPORT PORTJ +#define PJ3_DDR DDRJ +#define PJ3_PWM NULL +#undef PJ4 +#define PJ4_PIN PINJ4 +#define PJ4_RPORT PINJ +#define PJ4_WPORT PORTJ +#define PJ4_DDR DDRJ +#define PJ4_PWM NULL +#undef PJ5 +#define PJ5_PIN PINJ5 +#define PJ5_RPORT PINJ +#define PJ5_WPORT PORTJ +#define PJ5_DDR DDRJ +#define PJ5_PWM NULL +#undef PJ6 +#define PJ6_PIN PINJ6 +#define PJ6_RPORT PINJ +#define PJ6_WPORT PORTJ +#define PJ6_DDR DDRJ +#define PJ6_PWM NULL +#undef PJ7 +#define PJ7_PIN PINJ7 +#define PJ7_RPORT PINJ +#define PJ7_WPORT PORTJ +#define PJ7_DDR DDRJ +#define PJ7_PWM NULL + +#undef PK0 +#define PK0_PIN PINK0 +#define PK0_RPORT PINK +#define PK0_WPORT PORTK +#define PK0_DDR DDRK +#define PK0_PWM NULL +#undef PK1 +#define PK1_PIN PINK1 +#define PK1_RPORT PINK +#define PK1_WPORT PORTK +#define PK1_DDR DDRK +#define PK1_PWM NULL +#undef PK2 +#define PK2_PIN PINK2 +#define PK2_RPORT PINK +#define PK2_WPORT PORTK +#define PK2_DDR DDRK +#define PK2_PWM NULL +#undef PK3 +#define PK3_PIN PINK3 +#define PK3_RPORT PINK +#define PK3_WPORT PORTK +#define PK3_DDR DDRK +#define PK3_PWM NULL +#undef PK4 +#define PK4_PIN PINK4 +#define PK4_RPORT PINK +#define PK4_WPORT PORTK +#define PK4_DDR DDRK +#define PK4_PWM NULL +#undef PK5 +#define PK5_PIN PINK5 +#define PK5_RPORT PINK +#define PK5_WPORT PORTK +#define PK5_DDR DDRK +#define PK5_PWM NULL +#undef PK6 +#define PK6_PIN PINK6 +#define PK6_RPORT PINK +#define PK6_WPORT PORTK +#define PK6_DDR DDRK +#define PK6_PWM NULL +#undef PK7 +#define PK7_PIN PINK7 +#define PK7_RPORT PINK +#define PK7_WPORT PORTK +#define PK7_DDR DDRK +#define PK7_PWM NULL + +#undef PL0 +#define PL0_PIN PINL0 +#define PL0_RPORT PINL +#define PL0_WPORT PORTL +#define PL0_DDR DDRL +#define PL0_PWM NULL +#undef PL1 +#define PL1_PIN PINL1 +#define PL1_RPORT PINL +#define PL1_WPORT PORTL +#define PL1_DDR DDRL +#define PL1_PWM NULL +#undef PL2 +#define PL2_PIN PINL2 +#define PL2_RPORT PINL +#define PL2_WPORT PORTL +#define PL2_DDR DDRL +#define PL2_PWM NULL +#undef PL3 +#define PL3_PIN PINL3 +#define PL3_RPORT PINL +#define PL3_WPORT PORTL +#define PL3_DDR DDRL +#define PL3_PWM &OCR5AL +#undef PL4 +#define PL4_PIN PINL4 +#define PL4_RPORT PINL +#define PL4_WPORT PORTL +#define PL4_DDR DDRL +#define PL4_PWM &OCR5BL +#undef PL5 +#define PL5_PIN PINL5 +#define PL5_RPORT PINL +#define PL5_WPORT PORTL +#define PL5_DDR DDRL +#define PL5_PWM &OCR5CL +#undef PL6 +#define PL6_PIN PINL6 +#define PL6_RPORT PINL +#define PL6_WPORT PORTL +#define PL6_DDR DDRL +#define PL6_PWM NULL +#undef PL7 +#define PL7_PIN PINL7 +#define PL7_RPORT PINL +#define PL7_WPORT PORTL +#define PL7_DDR DDRL +#define PL7_PWM NULL + +#endif + +#if defined (__AVR_AT90USB1287__) || defined (__AVR_AT90USB1286__) +// SPI +#define SCK DIO9 +#define MISO DIO11 +#define MOSI DIO10 +#define SS DIO8 + +#if MOTHERBOARD!=8 && MOTHERBOARD!=9 +// change for your board +#define DEBUG_LED DIO31 /* led D5 red */ + +/* +pins +*/ +#define DIO0_PIN PINA0 +#define DIO0_RPORT PINA +#define DIO0_WPORT PORTA +#define DIO0_PWM NULL +#define DIO0_DDR DDRA + +#define DIO1_PIN PINA1 +#define DIO1_RPORT PINA +#define DIO1_WPORT PORTA +#define DIO1_PWM NULL +#define DIO1_DDR DDRA + +#define DIO2_PIN PINA2 +#define DIO2_RPORT PINA +#define DIO2_WPORT PORTA +#define DIO2_PWM NULL +#define DIO2_DDR DDRA + +#define DIO3_PIN PINA3 +#define DIO3_RPORT PINA +#define DIO3_WPORT PORTA +#define DIO3_PWM NULL +#define DIO3_DDR DDRA + +#define DIO4_PIN PINA4 +#define DIO4_RPORT PINA +#define DIO4_WPORT PORTA +#define DIO4_PWM NULL +#define DIO4_DDR DDRA + +#define DIO5_PIN PINA5 +#define DIO5_RPORT PINA +#define DIO5_WPORT PORTA +#define DIO5_PWM NULL +#define DIO5_DDR DDRA + +#define DIO6_PIN PINA6 +#define DIO6_RPORT PINA +#define DIO6_WPORT PORTA +#define DIO6_PWM NULL +#define DIO6_DDR DDRA + +#define DIO7_PIN PINA7 +#define DIO7_RPORT PINA +#define DIO7_WPORT PORTA +#define DIO7_PWM NULL +#define DIO7_DDR DDRA + +#define DIO8_PIN PINB0 +#define DIO8_RPORT PINB +#define DIO8_WPORT PORTB +#define DIO8_PWM NULL +#define DIO8_DDR DDRB + +#define DIO9_PIN PINB1 +#define DIO9_RPORT PINB +#define DIO9_WPORT PORTB +#define DIO9_PWM NULL +#define DIO9_DDR DDRB + +#define DIO10_PIN PINB2 +#define DIO10_RPORT PINB +#define DIO10_WPORT PORTB +#define DIO10_PWM NULL +#define DIO10_DDR DDRB + +#define DIO11_PIN PINB3 +#define DIO11_RPORT PINB +#define DIO11_WPORT PORTB +#define DIO11_PWM NULL +#define DIO11_DDR DDRB + +#define DIO12_PIN PINB4 +#define DIO12_RPORT PINB +#define DIO12_WPORT PORTB +#define DIO12_PWM NULL +#define DIO12_DDR DDRB + +#define DIO13_PIN PINB5 +#define DIO13_RPORT PINB +#define DIO13_WPORT PORTB +#define DIO13_PWM NULL +#define DIO13_DDR DDRB + +#define DIO14_PIN PINB6 +#define DIO14_RPORT PINB +#define DIO14_WPORT PORTB +#define DIO14_PWM NULL +#define DIO14_DDR DDRB + +#define DIO15_PIN PINB7 +#define DIO15_RPORT PINB +#define DIO15_WPORT PORTB +#define DIO15_PWM NULL +#define DIO15_DDR DDRB + +#define DIO16_PIN PINC0 +#define DIO16_RPORT PINC +#define DIO16_WPORT PORTC +#define DIO16_PWM NULL +#define DIO16_DDR DDRC + +#define DIO17_PIN PINC1 +#define DIO17_RPORT PINC +#define DIO17_WPORT PORTC +#define DIO17_PWM NULL +#define DIO17_DDR DDRC + +#define DIO18_PIN PINC2 +#define DIO18_RPORT PINC +#define DIO18_WPORT PORTC +#define DIO18_PWM NULL +#define DIO18_DDR DDRC + +#define DIO19_PIN PINC3 +#define DIO19_RPORT PINC +#define DIO19_WPORT PORTC +#define DIO19_PWM NULL +#define DIO19_DDR DDRC + +#define DIO20_PIN PINC4 +#define DIO20_RPORT PINC +#define DIO20_WPORT PORTC +#define DIO20_PWM NULL +#define DIO20_DDR DDRC + +#define DIO21_PIN PINC5 +#define DIO21_RPORT PINC +#define DIO21_WPORT PORTC +#define DIO21_PWM NULL +#define DIO21_DDR DDRC + +#define DIO22_PIN PINC6 +#define DIO22_RPORT PINC +#define DIO22_WPORT PORTC +#define DIO22_PWM NULL +#define DIO22_DDR DDRC + +#define DIO23_PIN PINC7 +#define DIO23_RPORT PINC +#define DIO23_WPORT PORTC +#define DIO23_PWM NULL +#define DIO23_DDR DDRC + +#define DIO24_PIN PIND0 +#define DIO24_RPORT PIND +#define DIO24_WPORT PORTD +#define DIO24_PWM NULL +#define DIO24_DDR DDRD + +#define DIO25_PIN PIND1 +#define DIO25_RPORT PIND +#define DIO25_WPORT PORTD +#define DIO25_PWM NULL +#define DIO25_DDR DDRD + +#define DIO26_PIN PIND2 +#define DIO26_RPORT PIND +#define DIO26_WPORT PORTD +#define DIO26_PWM NULL +#define DIO26_DDR DDRD + +#define DIO27_PIN PIND3 +#define DIO27_RPORT PIND +#define DIO27_WPORT PORTD +#define DIO27_PWM NULL +#define DIO27_DDR DDRD + +#define DIO28_PIN PIND4 +#define DIO28_RPORT PIND +#define DIO28_WPORT PORTD +#define DIO28_PWM NULL +#define DIO28_DDR DDRD + +#define DIO29_PIN PIND5 +#define DIO29_RPORT PIND +#define DIO29_WPORT PORTD +#define DIO29_PWM NULL +#define DIO29_DDR DDRD + +#define DIO30_PIN PIND6 +#define DIO30_RPORT PIND +#define DIO30_WPORT PORTD +#define DIO30_PWM NULL +#define DIO30_DDR DDRD + +#define DIO31_PIN PIND7 +#define DIO31_RPORT PIND +#define DIO31_WPORT PORTD +#define DIO31_PWM NULL +#define DIO31_DDR DDRD + + +#define DIO32_PIN PINE0 +#define DIO32_RPORT PINE +#define DIO32_WPORT PORTE +#define DIO32_PWM NULL +#define DIO32_DDR DDRE + +#define DIO33_PIN PINE1 +#define DIO33_RPORT PINE +#define DIO33_WPORT PORTE +#define DIO33_PWM NULL +#define DIO33_DDR DDRE + +#define DIO34_PIN PINE2 +#define DIO34_RPORT PINE +#define DIO34_WPORT PORTE +#define DIO34_PWM NULL +#define DIO34_DDR DDRE + +#define DIO35_PIN PINE3 +#define DIO35_RPORT PINE +#define DIO35_WPORT PORTE +#define DIO35_PWM NULL +#define DIO35_DDR DDRE + +#define DIO36_PIN PINE4 +#define DIO36_RPORT PINE +#define DIO36_WPORT PORTE +#define DIO36_PWM NULL +#define DIO36_DDR DDRE + +#define DIO37_PIN PINE5 +#define DIO37_RPORT PINE +#define DIO37_WPORT PORTE +#define DIO37_PWM NULL +#define DIO37_DDR DDRE + +#define DIO38_PIN PINE6 +#define DIO38_RPORT PINE +#define DIO38_WPORT PORTE +#define DIO38_PWM NULL +#define DIO38_DDR DDRE + +#define DIO39_PIN PINE7 +#define DIO39_RPORT PINE +#define DIO39_WPORT PORTE +#define DIO39_PWM NULL +#define DIO39_DDR DDRE + +#define AIO0_PIN PINF0 +#define AIO0_RPORT PINF +#define AIO0_WPORT PORTF +#define AIO0_PWM NULL +#define AIO0_DDR DDRF + +#define AIO1_PIN PINF1 +#define AIO1_RPORT PINF +#define AIO1_WPORT PORTF +#define AIO1_PWM NULL +#define AIO1_DDR DDRF + +#define AIO2_PIN PINF2 +#define AIO2_RPORT PINF +#define AIO2_WPORT PORTF +#define AIO2_PWM NULL +#define AIO2_DDR DDRF + +#define AIO3_PIN PINF3 +#define AIO3_RPORT PINF +#define AIO3_WPORT PORTF +#define AIO3_PWM NULL +#define AIO3_DDR DDRF + +#define AIO4_PIN PINF4 +#define AIO4_RPORT PINF +#define AIO4_WPORT PORTF +#define AIO4_PWM NULL +#define AIO4_DDR DDRF + +#define AIO5_PIN PINF5 +#define AIO5_RPORT PINF +#define AIO5_WPORT PORTF +#define AIO5_PWM NULL +#define AIO5_DDR DDRF + +#define AIO6_PIN PINF6 +#define AIO6_RPORT PINF +#define AIO6_WPORT PORTF +#define AIO6_PWM NULL +#define AIO6_DDR DDRF + +#define AIO7_PIN PINF7 +#define AIO7_RPORT PINF +#define AIO7_WPORT PORTF +#define AIO7_PWM NULL +#define AIO7_DDR DDRF + +#define DIO40_PIN PINF0 +#define DIO40_RPORT PINF +#define DIO40_WPORT PORTF +#define DIO40_PWM NULL +#define DIO40_DDR DDRF + +#define DIO41_PIN PINF1 +#define DIO41_RPORT PINF +#define DIO41_WPORT PORTF +#define DIO41_PWM NULL +#define DIO41_DDR DDRF + +#define DIO42_PIN PINF2 +#define DIO42_RPORT PINF +#define DIO42_WPORT PORTF +#define DIO42_PWM NULL +#define DIO42_DDR DDRF + +#define DIO43_PIN PINF3 +#define DIO43_RPORT PINF +#define DIO43_WPORT PORTF +#define DIO43_PWM NULL +#define DIO43_DDR DDRF + +#define DIO44_PIN PINF4 +#define DIO44_RPORT PINF +#define DIO44_WPORT PORTF +#define DIO44_PWM NULL +#define DIO44_DDR DDRF + +#define DIO45_PIN PINF5 +#define DIO45_RPORT PINF +#define DIO45_WPORT PORTF +#define DIO45_PWM NULL +#define DIO45_DDR DDRF + +#define DIO46_PIN PINF6 +#define DIO46_RPORT PINF +#define DIO46_WPORT PORTF +#define DIO46_PWM NULL +#define DIO46_DDR DDRF + +#define DIO47_PIN PINF7 +#define DIO47_RPORT PINF +#define DIO47_WPORT PORTF +#define DIO47_PWM NULL +#define DIO47_DDR DDRF + +#else +/* +pins +*/ +#define DIO0_PIN PIND0 +#define DIO0_RPORT PIND +#define DIO0_WPORT PORTD +#define DIO0_PWM NULL +#define DIO0_DDR DDRD + +#define DIO1_PIN PIND1 +#define DIO1_RPORT PIND +#define DIO1_WPORT PORTD +#define DIO1_PWM NULL +#define DIO1_DDR DDRD + +#define DIO2_PIN PIND2 +#define DIO2_RPORT PIND +#define DIO2_WPORT PORTD +#define DIO2_PWM NULL +#define DIO2_DDR DDRD + +#define DIO3_PIN PIND3 +#define DIO3_RPORT PIND +#define DIO3_WPORT PORTD +#define DIO3_PWM NULL +#define DIO3_DDR DDRD + +#define DIO4_PIN PIND4 +#define DIO4_RPORT PIND +#define DIO4_WPORT PORTD +#define DIO4_PWM NULL +#define DIO4_DDR DDRD + +#define DIO5_PIN PIND5 +#define DIO5_RPORT PIND +#define DIO5_WPORT PORTD +#define DIO5_PWM NULL +#define DIO5_DDR DDRD + +#define DIO6_PIN PIND6 +#define DIO6_RPORT PIND +#define DIO6_WPORT PORTD +#define DIO6_PWM NULL +#define DIO6_DDR DDRD + +#define DIO7_PIN PIND7 +#define DIO7_RPORT PIND +#define DIO7_WPORT PORTD +#define DIO7_PWM NULL +#define DIO7_DDR DDRD + +#define DIO8_PIN PINE0 +#define DIO8_RPORT PINE +#define DIO8_WPORT PORTE +#define DIO8_PWM NULL +#define DIO8_DDR DDRE + +#define DIO9_PIN PINE1 +#define DIO9_RPORT PINE +#define DIO9_WPORT PORTE +#define DIO9_PWM NULL +#define DIO9_DDR DDRE + +#define DIO10_PIN PINC0 +#define DIO10_RPORT PINC +#define DIO10_WPORT PORTC +#define DIO10_PWM NULL +#define DIO10_DDR DDRC + +#define DIO11_PIN PINC1 +#define DIO11_RPORT PINC +#define DIO11_WPORT PORTC +#define DIO11_PWM NULL +#define DIO11_DDR DDRC + +#define DIO12_PIN PINC2 +#define DIO12_RPORT PINC +#define DIO12_WPORT PORTC +#define DIO12_PWM NULL +#define DIO12_DDR DDRC + +#define DIO13_PIN PINC3 +#define DIO13_RPORT PINC +#define DIO13_WPORT PORTC +#define DIO13_PWM NULL +#define DIO13_DDR DDRC + +#define DIO14_PIN PINC4 +#define DIO14_RPORT PINC +#define DIO14_WPORT PORTC +#define DIO14_PWM NULL +#define DIO14_DDR DDRC + +#define DIO15_PIN PINC5 +#define DIO15_RPORT PINC +#define DIO15_WPORT PORTC +#define DIO15_PWM NULL +#define DIO15_DDR DDRC + +#define DIO16_PIN PINC6 +#define DIO16_RPORT PINC +#define DIO16_WPORT PORTC +#define DIO16_PWM NULL +#define DIO16_DDR DDRC + +#define DIO17_PIN PINC7 +#define DIO17_RPORT PINC +#define DIO17_WPORT PORTC +#define DIO17_PWM NULL +#define DIO17_DDR DDRC + +#define DIO18_PIN PINE6 +#define DIO18_RPORT PINE +#define DIO18_WPORT PORTE +#define DIO18_PWM NULL +#define DIO18_DDR DDRE + +#define DIO19_PIN PINE7 +#define DIO19_RPORT PINE +#define DIO19_WPORT PORTE +#define DIO19_PWM NULL +#define DIO19_DDR DDRE + +#define DIO20_PIN PINB0 +#define DIO20_RPORT PINB +#define DIO20_WPORT PORTB +#define DIO20_PWM NULL +#define DIO20_DDR DDRB + +#define DIO21_PIN PINB1 +#define DIO21_RPORT PINB +#define DIO21_WPORT PORTB +#define DIO21_PWM NULL +#define DIO21_DDR DDRB + +#define DIO22_PIN PINB2 +#define DIO22_RPORT PINB +#define DIO22_WPORT PORTB +#define DIO22_PWM NULL +#define DIO22_DDR DDRB + +#define DIO23_PIN PINB3 +#define DIO23_RPORT PINB +#define DIO23_WPORT PORTB +#define DIO23_PWM NULL +#define DIO23_DDR DDRB + +#define DIO24_PIN PINB4 +#define DIO24_RPORT PINB +#define DIO24_WPORT PORTB +#define DIO24_PWM NULL +#define DIO24_DDR DDRB + +#define DIO25_PIN PINB5 +#define DIO25_RPORT PINB +#define DIO25_WPORT PORTB +#define DIO25_PWM NULL +#define DIO25_DDR DDRB + +#define DIO26_PIN PINB6 +#define DIO26_RPORT PINB +#define DIO26_WPORT PORTB +#define DIO26_PWM NULL +#define DIO26_DDR DDRB + +#define DIO27_PIN PINB7 +#define DIO27_RPORT PINB +#define DIO27_WPORT PORTB +#define DIO27_PWM NULL +#define DIO27_DDR DDRB + +#define DIO28_PIN PINA0 +#define DIO28_RPORT PINA +#define DIO28_WPORT PORTA +#define DIO28_PWM NULL +#define DIO28_DDR DDRA + +#define DIO29_PIN PINA1 +#define DIO29_RPORT PINA +#define DIO29_WPORT PORTA +#define DIO29_PWM NULL +#define DIO29_DDR DDRA + +#define DIO30_PIN PINA2 +#define DIO30_RPORT PINA +#define DIO30_WPORT PORTA +#define DIO30_PWM NULL +#define DIO30_DDR DDRA + +#define DIO31_PIN PINA3 +#define DIO31_RPORT PINA +#define DIO31_WPORT PORTA +#define DIO31_PWM NULL +#define DIO31_DDR DDRA + + +#define DIO32_PIN PINA4 +#define DIO32_RPORT PINA +#define DIO32_WPORT PORTA +#define DIO32_PWM NULL +#define DIO32_DDR DDRA + +#define DIO33_PIN PINA5 +#define DIO33_RPORT PINA +#define DIO33_WPORT PORTA +#define DIO33_PWM NULL +#define DIO33_DDR DDRA + +#define DIO34_PIN PINA6 +#define DIO34_RPORT PINA +#define DIO34_WPORT PORTA +#define DIO34_PWM NULL +#define DIO34_DDR DDRA + +#define DIO35_PIN PINA7 +#define DIO35_RPORT PINA +#define DIO35_WPORT PORTA +#define DIO35_PWM NULL +#define DIO35_DDR DDRA + +#define DIO36_PIN PINE4 +#define DIO36_RPORT PINE +#define DIO36_WPORT PORTE +#define DIO36_PWM NULL +#define DIO36_DDR DDRE + +#define DIO37_PIN PINE5 +#define DIO37_RPORT PINE +#define DIO37_WPORT PORTE +#define DIO37_PWM NULL +#define DIO37_DDR DDRE + +#define DIO38_PIN PINF0 +#define DIO38_RPORT PINF +#define DIO38_WPORT PORTF +#define DIO38_PWM NULL +#define DIO38_DDR DDRF + +#define DIO39_PIN PINF1 +#define DIO39_RPORT PINF +#define DIO39_WPORT PORTF +#define DIO39_PWM NULL +#define DIO39_DDR DDRF + +#define DIO40_PIN PINF2 +#define DIO40_RPORT PINF +#define DIO40_WPORT PORTF +#define DIO40_PWM NULL +#define DIO40_DDR DDRF + +#define DIO41_PIN PINF3 +#define DIO41_RPORT PINF +#define DIO41_WPORT PORTF +#define DIO41_PWM NULL +#define DIO41_DDR DDRF + +#define DIO42_PIN PINF4 +#define DIO42_RPORT PINF +#define DIO42_WPORT PORTF +#define DIO42_PWM NULL +#define DIO42_DDR DDRF + +#define DIO43_PIN PINF5 +#define DIO43_RPORT PINF +#define DIO43_WPORT PORTF +#define DIO43_PWM NULL +#define DIO43_DDR DDRF + +#define DIO44_PIN PINF6 +#define DIO44_RPORT PINF +#define DIO44_WPORT PORTF +#define DIO44_PWM NULL +#define DIO44_DDR DDRF + +#define DIO45_PIN PINF7 +#define DIO45_RPORT PINF +#define DIO45_WPORT PORTF +#define DIO45_PWM NULL +#define DIO45_DDR DDRF + +#define AIO0_PIN PINF0 +#define AIO0_RPORT PINF +#define AIO0_WPORT PORTF +#define AIO0_PWM NULL +#define AIO0_DDR DDRF + +#define AIO1_PIN PINF1 +#define AIO1_RPORT PINF +#define AIO1_WPORT PORTF +#define AIO1_PWM NULL +#define AIO1_DDR DDRF + +#define AIO2_PIN PINF2 +#define AIO2_RPORT PINF +#define AIO2_WPORT PORTF +#define AIO2_PWM NULL +#define AIO2_DDR DDRF + +#define AIO3_PIN PINF3 +#define AIO3_RPORT PINF +#define AIO3_WPORT PORTF +#define AIO3_PWM NULL +#define AIO3_DDR DDRF + +#define AIO4_PIN PINF4 +#define AIO4_RPORT PINF +#define AIO4_WPORT PORTF +#define AIO4_PWM NULL +#define AIO4_DDR DDRF + +#define AIO5_PIN PINF5 +#define AIO5_RPORT PINF +#define AIO5_WPORT PORTF +#define AIO5_PWM NULL +#define AIO5_DDR DDRF + +#define AIO6_PIN PINF6 +#define AIO6_RPORT PINF +#define AIO6_WPORT PORTF +#define AIO6_PWM NULL +#define AIO6_DDR DDRF + +#define AIO7_PIN PINF7 +#define AIO7_RPORT PINF +#define AIO7_WPORT PORTF +#define AIO7_PWM NULL +#define AIO7_DDR DDRF + +//-- Begin not supported by Teensyduino +//-- don't use Arduino functions on these pins pinMode/digitalWrite/etc +#define DIO46_PIN PINE2 +#define DIO46_RPORT PINE +#define DIO46_WPORT PORTE +#define DIO46_PWM NULL +#define DIO46_DDR DDRE + +#define DIO47_PIN PINE3 +#define DIO47_RPORT PINE +#define DIO47_WPORT PORTE +#define DIO47_PWM NULL +#define DIO47_DDR DDRE +//-- end not supported by Teensyduino + +#endif + + +#undef PA0 +#define PA0_PIN PINA0 +#define PA0_RPORT PINA +#define PA0_WPORT PORTA +#define PA0_PWM NULL +#define PA0_DDR DDRA +#undef PA1 +#define PA1_PIN PINA1 +#define PA1_RPORT PINA +#define PA1_WPORT PORTA +#define PA1_PWM NULL +#define PA1_DDR DDRA +#undef PA2 +#define PA2_PIN PINA2 +#define PA2_RPORT PINA +#define PA2_WPORT PORTA +#define PA2_PWM NULL +#define PA2_DDR DDRA +#undef PA3 +#define PA3_PIN PINA3 +#define PA3_RPORT PINA +#define PA3_WPORT PORTA +#define PA3_PWM NULL +#define PA3_DDR DDRA +#undef PA4 +#define PA4_PIN PINA4 +#define PA4_RPORT PINA +#define PA4_WPORT PORTA +#define PA4_PWM NULL +#define PA4_DDR DDRA +#undef PA5 +#define PA5_PIN PINA5 +#define PA5_RPORT PINA +#define PA5_WPORT PORTA +#define PA5_PWM NULL +#define PA5_DDR DDRA +#undef PA6 +#define PA6_PIN PINA6 +#define PA6_RPORT PINA +#define PA6_WPORT PORTA +#define PA6_PWM NULL +#define PA6_DDR DDRA +#undef PA7 +#define PA7_PIN PINA7 +#define PA7_RPORT PINA +#define PA7_WPORT PORTA +#define PA7_PWM NULL +#define PA7_DDR DDRA + +#undef PB0 +#define PB0_PIN PINB0 +#define PB0_RPORT PINB +#define PB0_WPORT PORTB +#define PB0_PWM NULL +#define PB0_DDR DDRB +#undef PB1 +#define PB1_PIN PINB1 +#define PB1_RPORT PINB +#define PB1_WPORT PORTB +#define PB1_PWM NULL +#define PB1_DDR DDRB +#undef PB2 +#define PB2_PIN PINB2 +#define PB2_RPORT PINB +#define PB2_WPORT PORTB +#define PB2_PWM NULL +#define PB2_DDR DDRB +#undef PB3 +#define PB3_PIN PINB3 +#define PB3_RPORT PINB +#define PB3_WPORT PORTB +#define PB3_PWM NULL +#define PB3_DDR DDRB +#undef PB4 +#define PB4_PIN PINB4 +#define PB4_RPORT PINB +#define PB4_WPORT PORTB +#define PB4_PWM NULL +#define PB4_DDR DDRB +#undef PB5 +#define PB5_PIN PINB5 +#define PB5_RPORT PINB +#define PB5_WPORT PORTB +#define PB5_PWM NULL +#define PB5_DDR DDRB +#undef PB6 +#define PB6_PIN PINB6 +#define PB6_RPORT PINB +#define PB6_WPORT PORTB +#define PB6_PWM NULL +#define PB6_DDR DDRB +#undef PB7 +#define PB7_PIN PINB7 +#define PB7_RPORT PINB +#define PB7_WPORT PORTB +#define PB7_PWM NULL +#define PB7_DDR DDRB + +#undef PC0 +#define PC0_PIN PINC0 +#define PC0_RPORT PINC +#define PC0_WPORT PORTC +#define PC0_PWM NULL +#define PC0_DDR DDRC +#undef PC1 +#define PC1_PIN PINC1 +#define PC1_RPORT PINC +#define PC1_WPORT PORTC +#define PC1_PWM NULL +#define PC1_DDR DDRC +#undef PC2 +#define PC2_PIN PINC2 +#define PC2_RPORT PINC +#define PC2_WPORT PORTC +#define PC2_PWM NULL +#define PC2_DDR DDRC +#undef PC3 +#define PC3_PIN PINC3 +#define PC3_RPORT PINC +#define PC3_WPORT PORTC +#define PC3_PWM NULL +#define PC3_DDR DDRC +#undef PC4 +#define PC4_PIN PINC4 +#define PC4_RPORT PINC +#define PC4_WPORT PORTC +#define PC4_PWM NULL +#define PC4_DDR DDRC +#undef PC5 +#define PC5_PIN PINC5 +#define PC5_RPORT PINC +#define PC5_WPORT PORTC +#define PC5_PWM NULL +#define PC5_DDR DDRC +#undef PC6 +#define PC6_PIN PINC6 +#define PC6_RPORT PINC +#define PC6_WPORT PORTC +#define PC6_PWM NULL +#define PC6_DDR DDRC +#undef PC7 +#define PC7_PIN PINC7 +#define PC7_RPORT PINC +#define PC7_WPORT PORTC +#define PC7_PWM NULL +#define PC7_DDR DDRC + +#undef PD0 +#define PD0_PIN PIND0 +#define PD0_RPORT PIND +#define PD0_WPORT PORTD +#define PD0_PWM NULL +#define PD0_DDR DDRD +#undef PD1 +#define PD1_PIN PIND1 +#define PD1_RPORT PIND +#define PD1_WPORT PORTD +#define PD1_PWM NULL +#define PD1_DDR DDRD +#undef PD2 +#define PD2_PIN PIND2 +#define PD2_RPORT PIND +#define PD2_WPORT PORTD +#define PD2_PWM NULL +#define PD2_DDR DDRD +#undef PD3 +#define PD3_PIN PIND3 +#define PD3_RPORT PIND +#define PD3_WPORT PORTD +#define PD3_PWM NULL +#define PD3_DDR DDRD +#undef PD4 +#define PD4_PIN PIND4 +#define PD4_RPORT PIND +#define PD4_WPORT PORTD +#define PD4_PWM NULL +#define PD4_DDR DDRD +#undef PD5 +#define PD5_PIN PIND5 +#define PD5_RPORT PIND +#define PD5_WPORT PORTD +#define PD5_PWM NULL +#define PD5_DDR DDRD +#undef PD6 +#define PD6_PIN PIND6 +#define PD6_RPORT PIND +#define PD6_WPORT PORTD +#define PD6_PWM NULL +#define PD6_DDR DDRD +#undef PD7 +#define PD7_PIN PIND7 +#define PD7_RPORT PIND +#define PD7_WPORT PORTD +#define PD7_PWM NULL +#define PD7_DDR DDRD + +#undef PE0 +#define PE0_PIN PINE0 +#define PE0_RPORT PINE +#define PE0_WPORT PORTE +#define PE0_PWM NULL +#define PE0_DDR DDRE +#undef PE1 +#define PE1_PIN PINE1 +#define PE1_RPORT PINE +#define PE1_WPORT PORTE +#define PE1_PWM NULL +#define PE1_DDR DDRE +#undef PE2 +#define PE2_PIN PINE2 +#define PE2_RPORT PINE +#define PE2_WPORT PORTE +#define PE2_PWM NULL +#define PE2_DDR DDRE +#undef PE3 +#define PE3_PIN PINE3 +#define PE3_RPORT PINE +#define PE3_WPORT PORTE +#define PE3_PWM NULL +#define PE3_DDR DDRE +#undef PE4 +#define PE4_PIN PINE4 +#define PE4_RPORT PINE +#define PE4_WPORT PORTE +#define PE4_PWM NULL +#define PE4_DDR DDRE +#undef PE5 +#define PE5_PIN PINE5 +#define PE5_RPORT PINE +#define PE5_WPORT PORTE +#define PE5_PWM NULL +#define PE5_DDR DDRE +#undef PE6 +#define PE6_PIN PINE6 +#define PE6_RPORT PINE +#define PE6_WPORT PORTE +#define PE6_PWM NULL +#define PE6_DDR DDRE +#undef PE7 +#define PE7_PIN PINE7 +#define PE7_RPORT PINE +#define PE7_WPORT PORTE +#define PE7_PWM NULL +#define PE7_DDR DDRE + +#undef PF0 +#define PF0_PIN PINF0 +#define PF0_RPORT PINF +#define PF0_WPORT PORTF +#define PF0_PWM NULL +#define PF0_DDR DDRF +#undef PF1 +#define PF1_PIN PINF1 +#define PF1_RPORT PINF +#define PF1_WPORT PORTF +#define PF1_PWM NULL +#define PF1_DDR DDRF +#undef PF2 +#define PF2_PIN PINF2 +#define PF2_RPORT PINF +#define PF2_WPORT PORTF +#define PF2_PWM NULL +#define PF2_DDR DDRF +#undef PF3 +#define PF3_PIN PINF3 +#define PF3_RPORT PINF +#define PF3_WPORT PORTF +#define PF3_PWM NULL +#define PF3_DDR DDRF +#undef PF4 +#define PF4_PIN PINF4 +#define PF4_RPORT PINF +#define PF4_WPORT PORTF +#define PF4_PWM NULL +#define PF4_DDR DDRF +#undef PF5 +#define PF5_PIN PINF5 +#define PF5_RPORT PINF +#define PF5_WPORT PORTF +#define PF5_PWM NULL +#define PF5_DDR DDRF +#undef PF6 +#define PF6_PIN PINF6 +#define PF6_RPORT PINF +#define PF6_WPORT PORTF +#define PF6_PWM NULL +#define PF6_DDR DDRF +#undef PF7 +#define PF7_PIN PINF7 +#define PF7_RPORT PINF +#define PF7_WPORT PORTF +#define PF7_PWM NULL +#define PF7_DDR DDRF +#endif + +#ifndef DIO0_PIN +#error pins for this chip not defined in arduino.h! If you write an appropriate pin definition and have this firmware work on your chip, please submit a pull request +#endif + +#endif /* _ARDUINO_H */ + diff --git a/Repetier/gcode.cpp b/Repetier/gcode.cpp new file mode 100644 index 0000000..552982d --- /dev/null +++ b/Repetier/gcode.cpp @@ -0,0 +1,1012 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. + + Functions in this file are used to communicate using ascii or repetier protocol. +*/ + +#include "Reptier.h" + +#define bit_clear(x,y) x&= ~(1<head + 1) & SERIAL_BUFFER_MASK; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if (i != buffer->tail) { + buffer->buffer[buffer->head] = c; + buffer->head = i; + } +} +#if !defined(USART0_RX_vect) && defined(USART1_RX_vect) +// do nothing - on the 32u4 the first USART is USART1 +#else +#if !defined(USART_RX_vect) && !defined(SIG_USART0_RECV) && \ + !defined(SIG_UART0_RECV) && !defined(USART0_RX_vect) && \ + !defined(SIG_UART_RECV) + #error "Don't know what the Data Received vector is called for the first UART" +#else + void rfSerialEvent() __attribute__((weak)); + void rfSerialEvent() {} + #define serialEvent_implemented +#if defined(USART_RX_vect) + SIGNAL(USART_RX_vect) +#elif defined(SIG_USART0_RECV) + SIGNAL(SIG_USART0_RECV) +#elif defined(SIG_UART0_RECV) + SIGNAL(SIG_UART0_RECV) +#elif defined(USART0_RX_vect) + SIGNAL(USART0_RX_vect) +#elif defined(SIG_UART_RECV) + SIGNAL(SIG_UART_RECV) +#endif + { + #if defined(UDR0) + unsigned char c = UDR0; + #elif defined(UDR) + unsigned char c = UDR; + #else + #error UDR not defined + #endif + rf_store_char(c, &rx_buffer); + } +#endif +#endif + +#if !defined(USART0_UDRE_vect) && defined(USART1_UDRE_vect) +// do nothing - on the 32u4 the first USART is USART1 +#else +#if !defined(UART0_UDRE_vect) && !defined(UART_UDRE_vect) && !defined(USART0_UDRE_vect) && !defined(USART_UDRE_vect) + #error "Don't know what the Data Register Empty vector is called for the first UART" +#else +#if defined(UART0_UDRE_vect) +ISR(UART0_UDRE_vect) +#elif defined(UART_UDRE_vect) +ISR(UART_UDRE_vect) +#elif defined(USART0_UDRE_vect) +ISR(USART0_UDRE_vect) +#elif defined(USART_UDRE_vect) +ISR(USART_UDRE_vect) +#endif +{ + if (tx_buffer.head == tx_buffer.tail) { + // Buffer empty, so disable interrupts +#if defined(UCSR0B) + bit_clear(UCSR0B, UDRIE0); +#else + bit_clear(UCSRB, UDRIE); +#endif + } + else { + // There is more data in the output buffer. Send the next byte + unsigned char c = tx_buffer.buffer[tx_buffer.tail]; + tx_buffer.tail = (tx_buffer.tail + 1) & SERIAL_BUFFER_MASK; + + #if defined(UDR0) + UDR0 = c; + #elif defined(UDR) + UDR = c; + #else + #error UDR not defined + #endif + } +} +#endif +#endif + + +// Constructors //////////////////////////////////////////////////////////////// + +RFHardwareSerial::RFHardwareSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer, + volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, + volatile uint8_t *ucsra, volatile uint8_t *ucsrb, + volatile uint8_t *udr, + uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x) +{ + _rx_buffer = rx_buffer; + _tx_buffer = tx_buffer; + _ubrrh = ubrrh; + _ubrrl = ubrrl; + _ucsra = ucsra; + _ucsrb = ucsrb; + _udr = udr; + _rxen = rxen; + _txen = txen; + _rxcie = rxcie; + _udrie = udrie; + _u2x = u2x; +} + +// Public Methods ////////////////////////////////////////////////////////////// + +void RFHardwareSerial::begin(unsigned long baud) +{ + uint16_t baud_setting; + bool use_u2x = true; + +#if F_CPU == 16000000UL + // hardcoded exception for compatibility with the bootloader shipped + // with the Duemilanove and previous boards and the firmware on the 8U2 + // on the Uno and Mega 2560. + if (baud == 57600) { + use_u2x = false; + } +#endif + +try_again: + + if (use_u2x) { + *_ucsra = 1 << _u2x; + baud_setting = (F_CPU / 4 / baud - 1) / 2; + } else { + *_ucsra = 0; + baud_setting = (F_CPU / 8 / baud - 1) / 2; + } + + if ((baud_setting > 4095) && use_u2x) + { + use_u2x = false; + goto try_again; + } + + // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) + *_ubrrh = baud_setting >> 8; + *_ubrrl = baud_setting; + + bit_set(*_ucsrb, _rxen); + bit_set(*_ucsrb, _txen); + bit_set(*_ucsrb, _rxcie); + bit_clear(*_ucsrb, _udrie); +} + +void RFHardwareSerial::end() +{ + // wait for transmission of outgoing data + while (_tx_buffer->head != _tx_buffer->tail) + ; + + bit_clear(*_ucsrb, _rxen); + bit_clear(*_ucsrb, _txen); + bit_clear(*_ucsrb, _rxcie); + bit_clear(*_ucsrb, _udrie); + + // clear any received data + _rx_buffer->head = _rx_buffer->tail; +} + +int RFHardwareSerial::available(void) +{ + return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) & SERIAL_BUFFER_MASK; +} + +int RFHardwareSerial::peek(void) +{ + if (_rx_buffer->head == _rx_buffer->tail) { + return -1; + } else { + return _rx_buffer->buffer[_rx_buffer->tail]; + } +} + +int RFHardwareSerial::read(void) +{ + // if the head isn't ahead of the tail, we don't have any characters + if (_rx_buffer->head == _rx_buffer->tail) { + return -1; + } else { + unsigned char c = _rx_buffer->buffer[_rx_buffer->tail]; + _rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) & SERIAL_BUFFER_MASK; + return c; + } +} + +void RFHardwareSerial::flush() +{ + while (_tx_buffer->head != _tx_buffer->tail) + ; +} +#ifdef COMPAT_PRE1 + void +#else + size_t +#endif +RFHardwareSerial::write(uint8_t c) +{ + int i = (_tx_buffer->head + 1) & SERIAL_BUFFER_MASK; + + // If the output buffer is full, there's nothing for it other than to + // wait for the interrupt handler to empty it a bit + // ???: return 0 here instead? + while (i == _tx_buffer->tail) + ; + + _tx_buffer->buffer[_tx_buffer->head] = c; + _tx_buffer->head = i; + + bit_set(*_ucsrb, _udrie); +#ifndef COMPAT_PRE1 + return 1; +#endif +} + +// Preinstantiate Objects ////////////////////////////////////////////////////// + +#if defined(UBRRH) && defined(UBRRL) + RFHardwareSerial RFSerial(&rx_buffer, &tx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X); +#elif defined(UBRR0H) && defined(UBRR0L) + RFHardwareSerial RFSerial(&rx_buffer, &tx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0); +#elif defined(USBCON) + // do nothing - Serial object and buffers are initialized in CDC code +#else + #error no serial port defined (port 0) +#endif + +#endif + +/** \page Repetier-protocol + +\section Introduction + +The repetier-protocol was developed, to overcome some shortcommings in the standard +RepRap communication method, while remaining backward compatible. To use the improved +features of this protocal, you need a host which speaks it. On Windows the recommended +host software is Repetier-Host. It is developed in parallel to this firmware and supports +all implemented features. + +\subsection Improvements + +- With higher speeds, the serial connection is more likely to produce communication failures. + The standard method is to transfer a checksum at the end of the line. This checksum is the + XORd value of all characters send. The value is limited to a range between 0 and 127. It can + not detect two identical missing characters or a wrong order. Therefore the new protocol + uses Fletchers checksum, which overcomes these shortcommings. +- The new protocol send data in binary format. This reduces the data size to less then 50% and + it speeds up decoding the command. No slow conversion from string to floats are needed. + +*/ + +/** \brief Computes size of binary data from bitfield. + +In the repetier-protocol in binary mode, the first 2 bytes define the +data. From this bitfield, this function computes the size of the command +including the 2 bytes of the bitfield and the 2 bytes for the checksum. + +Gcode Letter to Bit and Datatype: + +- N : Bit 0 : 16-Bit Integer +- M : Bit 1 : 8-Bit unsigned byte +- G : Bit 2 : 8-Bit unsigned byte +- X : Bit 3 : 32-Bit Float +- Y : Bit 4 : 32-Bit Float +- Z : Bit 5 : 32-Bit Float +- E : Bit 6 : 32-Bit Float +- : Bit 7 : always set to distinguish binary from ASCII line. +- F : Bit 8 : 32-Bit Float +- T : Bit 9 : 8 Bit Integer +- S : Bit 10 : 32 Bit Value +- P : Bit 11 : 32 Bit Integer +- V2 : Bit 12 : Version 2 command for additional commands/sizes +- Ext : Bit 13 : There are 2 more bytes following with Bits, only for future versions +- Int :Bit 14 : Marks it as internal command, +- Text : Bit 15 : 16 Byte ASCII String terminated with 0 +Second word if V2: +- I : Bit 0 : 32-Bit float +- J : Bit 1 : 32-Bit float +- R : Bit 2 : 32-Bit float +*/ +byte gcode_comp_binary_size(char *ptr) {// unsigned int bitfield) { + byte s = 4; // include checksum and bitfield + unsigned int bitfield = *(int*)ptr; + if(bitfield & 1) s+=2; + if(bitfield & 8) s+=4; + if(bitfield & 16) s+=4; + if(bitfield & 32) s+=4; + if(bitfield & 64) s+=4; + if(bitfield & 256) s+=4; + if(bitfield & 512) s+=1; + if(bitfield & 1024) s+=4; + if(bitfield & 2048) s+=4; + if(bitfield & 4096) { // Version 2 or later + s+=2; // for bitfield 2 + unsigned int bitfield2 = *(int*)(ptr+2); + if(bitfield & 2) s+=2; + if(bitfield & 4) s+=2; + if(bitfield2 & 1) s+= 4; + if(bitfield2 & 2) s+= 4; + if(bitfield2 & 4) s+= 4; + if(bitfield & 32768) s+=(byte)ptr[4]+1; + //OUT_P_I_LN("LenStr:",(int)ptr[4]); + //OUT_P_I_LN("LenBinV2:",s); + } else { + if(bitfield & 2) s+=1; + if(bitfield & 4) s+=1; + if(bitfield & 32768) s+=16; + } + return s; +} + +extern "C" void __cxa_pure_virtual() { } + +SerialOutput::SerialOutput() { +} +#ifdef COMPAT_PRE1 +void +#else +size_t +#endif +SerialOutput::write(uint8_t value) { + RFSERIAL.write(value); +#ifndef COMPAT_PRE1 + return 1; +#endif +} +void SerialOutput::printFloat(double number, uint8_t digits) +{ + if (isnan(number)) { + print_P(PSTR("NAN")); + return; + } + + if (isinf(number)) { + print_P(PSTR("INF")); + return; + } + + // Handle negative numbers + if (number < 0.0) + { + write('-'); + number = -number; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + double rounding = 0.5; + for (uint8_t i=0; i 0) + write('.'); + + // Extract digits from the remainder one at a time + while (digits-- > 0) + { + remainder *= 10.0; + int toPrint = int(remainder); + print(toPrint); + remainder -= toPrint; + } +} + +/** + Print a string stored in program memory on serial console. + + Example: serial_print_pgm(PSTR("Dummy string")); +*/ +void SerialOutput::print_P(PGM_P ptr) { + char c; + while ((c=pgm_read_byte(ptr++)) != 0x00) + write(c); +} + +/** + Print a string stored in program memory on serial console. + + Example: out.println_P(PSTR("Dummy string")); +*/ +void SerialOutput::println_P(PGM_P ptr) { + print_P(ptr); + println(); +} +void SerialOutput::print_long_P(PGM_P ptr,long value) { + print_P(ptr); + print(value); +} +void SerialOutput::print_int_P(PGM_P ptr,int value) { + print_P(ptr); + print(value); +} + +void SerialOutput::print_float_P(PGM_P ptr,float value,uint8_t digits) { + print_P(ptr); + out.printFloat(value,digits); +} +void SerialOutput::println_long_P(PGM_P ptr,long value) { + print_P(ptr); + println(value); +} +void SerialOutput::println_int_P(PGM_P ptr,int value) { + print_P(ptr); + println(value); +} + +void SerialOutput::println_float_P(PGM_P ptr,float value,uint8_t digits) { + print_P(ptr); + printFloat(value,digits); + println(); +} +void SerialOutput::print_error_P(PGM_P ptr,bool newline) { + OUT_P("error:"); + print_P(ptr); + if(newline) + println(); +} +/** \brief request resend of the expected line. +*/ +void gcode_resend() { + RFSERIAL.flush(); + gcode_wpos=0; + if(gcode_binary) + gcode_wait_resend = 30; + else + gcode_wait_resend = 14; + OUT_LN; + OUT_P_L_LN("Resend:",gcode_lastN+1); + OUT_P_LN("ok"); +} +void emergencyStop() { +#if defined(KILL_METHOD) && KILL_METHOD==1 + resetFunc(); +#else + cli(); // Don't allow interrupts to do their work + kill(false); + manage_temperatures(); + pwm_pos[0] = pwm_pos[1] = pwm_pos[2] = pwm_pos[3]=0; +#if EXT0_HEATER_PIN>-1 + WRITE(EXT0_HEATER_PIN,0); +#endif +#if defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN>-1 + WRITE(EXT1_HEATER_PIN,0); +#endif +#if defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN>-1 + WRITE(EXT2_HEATER_PIN,0); +#endif +#if FAN_PIN>-1 + WRITE(FAN_PIN,0); +#endif + while(1) {} +#endif +} +/** + Check if result is plausible. If it is, an ok is send and the command is stored in queue. + If not, a resend and ok is send. +*/ +void gcode_checkinsert(GCode *act) { + if(GCODE_HAS_M(act)) { + if(act->M==110) { // Reset line number + gcode_lastN = gcode_actN; + OUT_P_LN("ok"); + return; + } + if(act->M==112) { // Emergency kill - freeze printer + emergencyStop(); + } + } + if(GCODE_HAS_N(act)) { + if((((gcode_lastN+1) & 0xffff)!=(gcode_actN&0xffff))) { + if(gcode_wait_resend<0) { // after a resend, we have to skip the garbage in buffers, no message for this + if(DEBUG_ERRORS) { + OUT_P_L("Error: expected line ",gcode_lastN+1); + OUT_P_L_LN(" got ",gcode_actN); + } + gcode_resend(); // Line missing, force resend + } else { + --gcode_wait_resend; + gcode_wpos = 0; + OUT_P_L_LN("skip ",gcode_actN); + OUT_P_LN("ok"); + } + return; + } + gcode_lastN = gcode_actN; + } + gcode_windex = (gcode_windex+1) % GCODE_BUFFER_SIZE; + gcode_buflen++; +#ifdef ACK_WITH_LINENUMBER + OUT_P_L_LN("ok ",gcode_actN); +#else + OUT_P_LN("ok"); +#endif + gcode_last_binary = gcode_binary; + gcode_wait_resend = -1; // everything is ok. +#ifndef ECHO_ON_EXECUTE + if(DEBUG_ECHO) { + OUT_P("Echo:"); + gcode_print_command(act); + out.println(); + } +#endif +} +void gcode_silent_insert() { + gcode_windex = (gcode_windex+1) % GCODE_BUFFER_SIZE; + gcode_buflen++; +#ifndef ECHO_ON_EXECUTE + if(DEBUG_ECHO) { + out.print_P(PSTR("Echo:")); + gcode_print_command(act); + out.println(); + } +#endif +} +/** + Get the next buffered command. Returns 0 if no more commands are buffered. For each + returned command, the gcode_command_finished() function must be called. +*/ +GCode *gcode_next_command() { + if(gcode_buflen==0) return 0; // No more data + byte idx = gcode_rindex; + gcode_rindex = (idx+1) % GCODE_BUFFER_SIZE; + return &gcode_buffer[idx]; +} +/** \brief Removes the last returned command from cache. + +*/ +void gcode_command_finished(GCode *code) { + if(!gcode_buflen) return; // Should not happen, but safety first +#ifdef ECHO_ON_EXECUTE + if(DEBUG_ECHO) { + OUT_P("Echo:"); + gcode_print_command(code); + out.println(); + } +#endif + gcode_buflen--; +} + +/** \brief Execute commands in progmem stored string. Multiple commands are seperated by \n */ +void gcode_execute_PString(PGM_P cmd) { + char buf[80]; + byte buflen; + char c; + GCode code; + do { + // Wait for a free place in command buffer + // Scan next command from string + byte comment=0; + buflen = 0; + do { + c = pgm_read_byte(cmd++); + if(c == 0 || c == '\n') break; + if(c == ';') comment = 1; + if(comment) continue; + buf[buflen++] = c; + } while(buflen<79); + if(buflen==0) { // empty line ignore + continue; + } + buf[buflen]=0; + // Send command into command buffer + if(gcode_parse_ascii(&code,(char *)buf) && (code.params & 518)) { // Success + process_command(&code,false); + defaultLoopActions(); + } + } while(c); +} +/** \brief Read from serial console or sdcard. + +This function is the main function to read the commands from serial console or from sdcard. +It must be called frequently to empty the incoming buffer. +*/ +void gcode_read_serial() { + if(gcode_wait_all_parsed && gcode_buflen) return; + gcode_wait_all_parsed=false; + GCode *act; + unsigned long time = millis(); + if(gcode_buflen>=GCODE_BUFFER_SIZE) return; // all buffers full + if(RFSERIAL.available()==0) { + if((gcode_wait_resend>=0 || gcode_wpos>0) && time-gcode_lastdata>200) { + gcode_resend(); // Something is wrong, a started line was not continued in the last second + gcode_lastdata = time; + } +#ifdef WAITING_IDENTIFIER + else if(gcode_buflen == 0 && time-gcode_lastdata>1000) { // Don't do it if buffer is not empty. It may be a slow executing command. + OUT_P_LN(WAITING_IDENTIFIER); // Unblock communication in case the last ok was not received correct. + gcode_lastdata = time; + } +#endif + } + while(RFSERIAL.available() > 0 && gcode_wpos < MAX_CMD_SIZE) { // consume data until no data or buffer full + gcode_lastdata = millis(); + gcode_transbuffer[gcode_wpos++] = RFSERIAL.read(); + // first lets detect, if we got an old type ascii command + if(gcode_wpos==1) { + if(gcode_wait_resend>=0 && gcode_last_binary) { + if(!gcode_transbuffer[0]) {gcode_wait_resend--;} // Skip 30 zeros to get in sync + else gcode_wait_resend = 30; + gcode_wpos = 0; + continue; + } + if(!gcode_transbuffer[0]) {gcode_wpos = 0;continue;} + gcode_binary = (gcode_transbuffer[0] & 128)!=0; + } + if(gcode_binary) { + if(gcode_wpos < 2 ) continue; + if(gcode_wpos == 5) gcode_binary_size = gcode_comp_binary_size((char*)gcode_transbuffer); + if(gcode_wpos==gcode_binary_size) { + act = &gcode_buffer[gcode_windex]; + if(gcode_parse_binary(act,gcode_transbuffer)) { // Success + gcode_checkinsert(act); + } else { + gcode_resend(); + } + gcode_wpos = 0; + return; + } + } else { // Ascii command + char ch = gcode_transbuffer[gcode_wpos-1]; + if(ch == '\n' || ch == '\r' || ch == ':' || gcode_wpos >= (MAX_CMD_SIZE - 1) ) {// complete line read + gcode_transbuffer[gcode_wpos]=0; + gcode_comment = false; + if(gcode_wpos==1) { // empty line ignore + gcode_wpos = 0; + continue; + } + act = &gcode_buffer[gcode_windex]; + if(gcode_parse_ascii(act,(char *)gcode_transbuffer)) { // Success + gcode_checkinsert(act); + } else { + gcode_resend(); + } + gcode_wpos = 0; + return; + } else { + if(ch == ';') gcode_comment = true; // ignore new data until lineend + if(gcode_comment) gcode_wpos--; + } + } + } + #if SDSUPPORT + if(!sd.sdmode || gcode_wpos!=0) { // not reading or incoming serial command + return; + } + while( sd.filesize > sd.sdpos && gcode_wpos < MAX_CMD_SIZE) { // consume data until no data or buffer full + gcode_lastdata = millis(); + int n = sd.file.read(); + if(n==-1) { + OUT_P_LN("SD read error"); + break; + } + sd.sdpos++; // = file.curPosition(); + gcode_transbuffer[gcode_wpos++] = (byte)n; + + // first lets detect, if we got an old type ascii command + if(gcode_wpos==1) { + gcode_binary = (gcode_transbuffer[0] & 128)!=0; + } + if(gcode_binary) { + if(gcode_wpos < 2 ) continue; + if(gcode_wpos == 5) gcode_binary_size = gcode_comp_binary_size((char*)gcode_transbuffer); + if(gcode_wpos==gcode_binary_size) { + act = &gcode_buffer[gcode_windex]; + if(gcode_parse_binary(act,gcode_transbuffer)) { // Success + gcode_silent_insert(); + } + gcode_wpos = 0; + return; + } + } else { + char ch = gcode_transbuffer[gcode_wpos-1]; + if(ch == '\n' || ch == '\r' || ch == ':' || gcode_wpos >= (MAX_CMD_SIZE - 1) ) {// complete line read + gcode_transbuffer[gcode_wpos]=0; + gcode_comment = false; + if(gcode_wpos==1) { // empty line ignore + gcode_wpos = 0; + continue; + } + act = &gcode_buffer[gcode_windex]; + if(gcode_parse_ascii(act,(char *)gcode_transbuffer)) { // Success + gcode_silent_insert(); + } + gcode_wpos = 0; + return; + } else { + if(ch == ';') gcode_comment = true; // ignore new data until lineend + if(gcode_comment) gcode_wpos--; + } + } + } + sd.sdmode = false; + OUT_P_LN("Done printing file"); + /*OUT_P_L("Printed bytes:",sd.sdpos); + OUT_P_L_LN(" of ",sd.filesize); + OUT_P_I_LN("WPOS:",gcode_wpos);*/ + gcode_wpos = 0; +#endif +} + +/** + Converts a binary bytefield containing one GCode line into a GCode structure. + Returns true if checksum was correct. +*/ +bool gcode_parse_binary(GCode *code,byte *buffer) { + unsigned int sum1=0,sum2=0; // for fletcher-16 checksum + // first do fletcher-16 checksum tests see + // http://en.wikipedia.org/wiki/Fletcher's_checksum + byte i=0; + byte *p = buffer; + byte len = gcode_binary_size-2; + while (len) { + byte tlen = len > 21 ? 21 : len; + len -= tlen; + do { + sum1 += *p++; + if(sum1>=255) sum1-=255; + sum2 += sum1; + if(sum2>=255) sum2-=255; + } while (--tlen); + } + sum1 -= *p++; + sum2 -= *p; + if(sum1 | sum2) { + if(DEBUG_ERRORS) { + OUT_P_LN("Error:Binary cmd wrong checksum."); + } + return false; + } + p = buffer; + code->params = *(unsigned int *)p;p+=2; + byte textlen=16; + if(GCODE_IS_V2(code)) { + code->params2 = *(unsigned int *)p;p+=2; + if(GCODE_HAS_STRING(code)) + textlen = *p++; + } else code->params2 = 0; + if(code->params & 1) {gcode_actN=code->N=*(unsigned int *)p;p+=2;} + if(GCODE_IS_V2(code)) { // Read G,M as 16 bit value + if(code->params & 2) {code->M=*(unsigned int *)p;p+=2;} + if(code->params & 4) {code->G=*(unsigned int *)p;p+=2;} + } else { + if(code->params & 2) {code->M=*p++;} + if(code->params & 4) {code->G=*p++;} + } + //if(code->params & 8) {memcpy(&code->X,p,4);p+=4;} + if(code->params & 8) {code->X=*(float *)p;p+=4;} + if(code->params & 16) {code->Y=*(float *)p;p+=4;} + if(code->params & 32) {code->Z =*(float *)p;p+=4;} + if(code->params & 64) {code->E=*(float *)p;p+=4;} + if(code->params & 256) {code->F=*(float *)p;p+=4;} + if(code->params & 512) {code->T=*p++;} + if(code->params & 1024) {code->S=*(long int*)p;p+=4;} + if(code->params & 2048) {code->P=*(long int*)p;p+=4;} + if(GCODE_HAS_I(code)) {code->I=*(float *)p;p+=4;} + if(GCODE_HAS_J(code)) {code->J=*(float *)p;p+=4;} + if(GCODE_HAS_R(code)) {code->R=*(float *)p;p+=4;} + if(GCODE_HAS_STRING(code)) { // set text pointer to string + code->text = (char*)p; + code->text[textlen] = 0; // Terminate string overwriting checksum + gcode_wait_all_parsed=true; // Don't destroy string until executed + } + return true; +} +inline float gcode_value(char *s) { return (strtod(s, NULL)); } +inline long gcode_value_long(char *s) { return (strtol(s, NULL, 10)); } + +/** + Converts a ascii GCode line into a GCode structure. +*/ +bool gcode_parse_ascii(GCode *code,char *line) { + bool has_checksum = false; + char *pos; + code->params = 0; + code->params2 = 0; + if((pos = strchr(line,'N'))!=0) { // Line number detected + gcode_actN = gcode_value_long(++pos); + code->params |=1; + code->N = gcode_actN & 0xffff; + } + if((pos = strchr(line,'M'))!=0) { // M command + code->M = gcode_value_long(++pos) & 0xffff; + code->params |= 2; + if(code->M>255) code->params |= 4096; + } + if(GCODE_HAS_M(code) && (code->M == 23 || code->M == 28 || code->M == 29 || code->M == 30 || code->M == 32 || code->M == 117)) { + // after M command we got a filename for sd card management + char *sp = line; + while(*sp!='M') sp++; // Search M command + while(*sp!=' ') sp++; // search next whitespace + while(*sp==' ') sp++; // skip leading whitespaces + //char *wp = code->text; + code->text = sp; + while(*sp) { + if(code->M != 117 && (*sp==' ' || *sp=='*')) break; // end of filename reached + sp++; + //*wp++ = *sp++; + } + //*wp = 0; // ensure ending 0 + *sp = 0; // Removes checksum, but we don't care. Could also be part of the string. + gcode_wait_all_parsed = true; // don't risk string be deleted + code->params |= 32768; + } else { + if((pos = strchr(line,'G'))!=0) { // G command + code->G = gcode_value_long(++pos) & 0xffff; + code->params |= 4; + if(code->G>255) code->params |= 4096; + } + if((pos = strchr(line,'X'))!=0) { + code->X = gcode_value(++pos); + code->params |= 8; + } + if((pos = strchr(line,'Y'))!=0) { + code->Y = gcode_value(++pos); + code->params |= 16; + } + if((pos = strchr(line,'Z'))!=0) { + code->Z = gcode_value(++pos); + code->params |= 32; + } + if((pos = strchr(line,'E'))!=0) { + code->E = gcode_value(++pos); + code->params |= 64; + } + if((pos = strchr(line,'F'))!=0) { + code->F = gcode_value(++pos); + code->params |= 256; + } + if((pos = strchr(line,'T'))!=0) { // M command + code->T = gcode_value_long(++pos) & 0xff; + code->params |= 512; + } + if((pos = strchr(line,'S'))!=0) { // M command + code->S = gcode_value_long(++pos); + code->params |= 1024; + } + if((pos = strchr(line,'P'))!=0) { // M command + code->P = gcode_value_long(++pos); + code->params |= 2048; + } + if((pos = strchr(line,'I'))!=0) { + code->I = gcode_value(++pos); + code->params2 |= 1; + code->params |= 4096; // Needs V2 for saving + } + if((pos = strchr(line,'J'))!=0) { + code->J = gcode_value(++pos); + code->params2 |= 2; + code->params |= 4096; // Needs V2 for saving + } + if((pos = strchr(line,'R'))!=0) { + code->R = gcode_value(++pos); + code->params2 |= 4; + code->params |= 4096; // Needs V2 for saving + } + } + if((pos = strchr(line,'*'))!=0) { // checksum + byte checksum_given = gcode_value_long(pos+1); + byte checksum = 0; + while(line!=pos) checksum ^= *line++; + if(checksum!=checksum_given) { + if(DEBUG_ERRORS) { + out.println_int_P(PSTR("Error: Wrong checksum "),(int)checksum); + } + return false; // mismatch + } + } + return true; +} + +/** \brief Print command on serial console */ +void gcode_print_command(GCode *code) { + if(GCODE_HAS_M(code)) { + out.print('M'); + out.print((int)code->M); + out.print(' '); + } + if(GCODE_HAS_G(code)) { + out.print('G'); + out.print((int)code->G); + out.print(' '); + } + if(GCODE_HAS_T(code)) { + out.print('T'); + out.print((int)code->T); + out.print(' '); + } + if(GCODE_HAS_X(code)) { + out.print_float_P(PSTR(" X"),code->X); + } + if(GCODE_HAS_Y(code)) { + out.print_float_P(PSTR(" Y"),code->Y); + } + if(GCODE_HAS_Z(code)) { + out.print_float_P(PSTR(" Z"),code->Z); + } + if(GCODE_HAS_E(code)) { + out.print_float_P(PSTR(" E"),code->E,4); + } + if(GCODE_HAS_F(code)) { + out.print_float_P(PSTR(" F"),code->F); + } + if(GCODE_HAS_S(code)) { + out.print_long_P(PSTR(" S"),code->S); + } + if(GCODE_HAS_P(code)) { + out.print_long_P(PSTR(" P"),code->P); + } + if(GCODE_HAS_I(code)) { + out.print_float_P(PSTR(" I"),code->I); + } + if(GCODE_HAS_J(code)) { + out.print_float_P(PSTR(" J"),code->J); + } + if(GCODE_HAS_R(code)) { + out.print_float_P(PSTR(" R"),code->R); + } + if(GCODE_HAS_STRING(code)) { + out.print(' '); + out.print(code->text); + } +} diff --git a/Repetier/gcode.h b/Repetier/gcode.h new file mode 100644 index 0000000..ef706d8 --- /dev/null +++ b/Repetier/gcode.h @@ -0,0 +1,201 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + +*/ +#ifndef _GCODE_H +#define _GCODE_H + +#include +// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734 +//#ifdef PROGMEM +//#undef PROGMEM +//#define PROGMEM __attribute__((section(".progmem.data"))) +//#endif + +typedef struct { // 52 bytes per command needed + unsigned int params; + unsigned int params2; + unsigned int N; // Line number + unsigned int M; + unsigned int G; + float X; + float Y; + float Z; + float E; + float F; + byte T; + long S; + long P; + float I; + float J; + float R; + char *text; //text[17]; +} GCode; + +#ifndef EXTERNALSERIAL +// Implement serial communication for one stream only! +/* + HardwareSerial.h - Hardware serial library for Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 28 September 2010 by Mark Sproul + + Modified to use only 1 queue with fixed length by Repetier +*/ + +#define SERIAL_BUFFER_SIZE 128 +#define SERIAL_BUFFER_MASK 127 + +struct ring_buffer +{ + unsigned char buffer[SERIAL_BUFFER_SIZE]; + volatile int head; + volatile int tail; +}; +class RFHardwareSerial : public Print +{ + public: + ring_buffer *_rx_buffer; + ring_buffer *_tx_buffer; + volatile uint8_t *_ubrrh; + volatile uint8_t *_ubrrl; + volatile uint8_t *_ucsra; + volatile uint8_t *_ucsrb; + volatile uint8_t *_udr; + uint8_t _rxen; + uint8_t _txen; + uint8_t _rxcie; + uint8_t _udrie; + uint8_t _u2x; + public: + RFHardwareSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer, + volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, + volatile uint8_t *ucsra, volatile uint8_t *ucsrb, + volatile uint8_t *udr, + uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x); + void begin(unsigned long); + void end(); + virtual int available(void); + virtual int peek(void); + virtual int read(void); + virtual void flush(void); +#ifdef COMPAT_PRE1 + virtual void write(uint8_t); +#else + virtual size_t write(uint8_t); +#endif + using Print::write; // pull in write(str) and write(buf, size) from Print + operator bool(); +}; +extern RFHardwareSerial RFSerial; +#define RFSERIAL RFSerial +extern ring_buffer tx_buffer; +#define WAIT_OUT_EMPTY while(tx_buffer.head != tx_buffer.tail) {} +#else +#define RFSERIAL Serial +#endif + +class SerialOutput : public Print { +public: + SerialOutput(); +#ifdef COMPAT_PRE1 + void write(uint8_t); +#else + size_t write(uint8_t); +#endif + void print_P(PGM_P ptr); + void println_P(PGM_P ptr); + void print_long_P(PGM_P ptr,long value); + void print_int_P(PGM_P ptr,int value); + void print_float_P(PGM_P ptr,float value,uint8_t digits = 2); + void println_long_P(PGM_P ptr,long value); + void println_int_P(PGM_P ptr,int value); + void println_float_P(PGM_P ptr,float value,uint8_t digits = 2); + void print_error_P(PGM_P ptr,bool newline); + void printFloat(double number, uint8_t digits=2); + +}; +#define OUT_P_I(p,i) out.print_int_P(PSTR(p),(int)(i)) +#define OUT_P_I_LN(p,i) out.println_int_P(PSTR(p),(int)(i)) +#define OUT_P_L(p,i) out.print_long_P(PSTR(p),(long)(i)) +#define OUT_P_L_LN(p,i) out.println_long_P(PSTR(p),(long)(i)) +#define OUT_P_F(p,i) out.print_float_P(PSTR(p),(float)(i)) +#define OUT_P_F_LN(p,i) out.println_float_P(PSTR(p),(float)(i)) +#define OUT_P_FX(p,i,x) out.print_float_P(PSTR(p),(float)(i),x) +#define OUT_P_FX_LN(p,i,x) out.println_float_P(PSTR(p),(float)(i),x) +#define OUT_P(p) out.print_P(PSTR(p)) +#define OUT_P_LN(p) out.println_P(PSTR(p)) +#define OUT_ERROR_P(p) out.print_error_P(PSTR(p),false) +#define OUT_ERROR_P_LN(p) out.print_error_P(PSTR(p),true) +#define OUT(v) out.print(v) +#define OUT_LN out.println() +extern SerialOutput out; +/** Get next command in command buffer. After the command is processed, call gcode_command_finished() */ +extern GCode *gcode_next_command(); +/** Frees the cache used by the last command fetched. */ +extern void gcode_command_finished(GCode *code); +// check for new commands +extern void gcode_read_serial(); +extern void gcode_execute_PString(PGM_P cmd); +extern void gcode_print_command(GCode *code); +extern byte gcode_comp_binary_size(char *ptr); +extern bool gcode_parse_binary(GCode *code,byte *buffer); +extern bool gcode_parse_ascii(GCode *code,char *line); +extern void emergencyStop(); + +// Helper macros to detect, if parameter is stored in GCode struct +#define GCODE_HAS_N(a) ((a->params & 1)!=0) +#define GCODE_HAS_M(a) ((a->params & 2)!=0) +#define GCODE_HAS_G(a) ((a->params & 4)!=0) +#define GCODE_HAS_X(a) ((a->params & 8)!=0) +#define GCODE_HAS_Y(a) ((a->params & 16)!=0) +#define GCODE_HAS_Z(a) ((a->params & 32)!=0) +#define GCODE_HAS_NO_XYZ(a) ((a->params & 56)==0) +#define GCODE_HAS_E(a) ((a->params & 64)!=0) +#define GCODE_HAS_F(a) ((a->params & 256)!=0) +#define GCODE_HAS_T(a) ((a->params & 512)!=0) +#define GCODE_HAS_S(a) ((a->params & 1024)!=0) +#define GCODE_HAS_P(a) ((a->params & 2048)!=0) +#define GCODE_IS_V2(a) ((a->params & 4096)!=0) +#define GCODE_HAS_STRING(a) ((a->params & 32768)!=0) +#define GCODE_HAS_I(a) ((a->params2 & 1)!=0) +#define GCODE_HAS_J(a) ((a->params2 & 2)!=0) +#define GCODE_HAS_R(a) ((a->params2 & 4)!=0) + +extern byte debug_level; +#define DEBUG_ECHO ((debug_level & 1)!=0) +#define DEBUG_INFO ((debug_level & 2)!=0) +#define DEBUG_ERRORS ((debug_level & 4)!=0) +#define DEBUG_DRYRUN ((debug_level & 8)!=0) +#define DEBUG_COMMUNICATION ((debug_level & 16)!=0) +#define DEBUG_NO_MOVES ((debug_level & 32)!=0) + +#endif + diff --git a/Repetier/motion.cpp b/Repetier/motion.cpp new file mode 100644 index 0000000..462eb26 --- /dev/null +++ b/Repetier/motion.cpp @@ -0,0 +1,1371 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + + This firmware is a nearly complete rewrite of the sprinter firmware + by kliment (https://github.com/kliment/Sprinter) + which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware. + + Functions in this file are used to communicate using ascii or repetier protocol. +*/ + +#include "Reptier.h" +#include "ui.h" + +// ########################################################################## +// ### Path planner stuff ### +// ########################################################################## + +inline unsigned long U16SquaredToU32(unsigned int val) { + long res; + __asm__ __volatile__ ( // 15 Ticks + "mul %A1,%A1 \n\t" + "movw %A0,r0 \n\t" + "mul %B1,%B1 \n\t" + "movw %C0,r0 \n\t" + "mul %A1,%B1 \n\t" + "clr %A1 \n\t" + "add %B0,r0 \n\t" + "adc %C0,r1 \n\t" + "adc %D0,%A1 \n\t" + "add %B0,r0 \n\t" + "adc %C0,r1 \n\t" + "adc %D0,%A1 \n\t" + "clr r1 \n\t" + : "=&r"(res),"=r"(val) + : "1"(val) + ); + return res; +} +/** +Computes the maximum junction speed + +p1 = previous segment +p2 = new segment +*/ +inline void computeMaxJunctionSpeed(PrintLine *p1,PrintLine *p2) { + if(p1->flags & FLAG_WARMUP) { + p2->joinFlags |= FLAG_JOIN_START_FIXED; + return; + } +#if DRIVE_SYSTEM==3 + if (p1->moveID == p2->moveID) { // Avoid computing junction speed for split delta lines + if(p1->fullSpeed>p2->fullSpeed) + p1->maxJunctionSpeed = p2->fullSpeed; + else + p1->maxJunctionSpeed = p1->fullSpeed; + return; + } +#endif + // First we compute the normalized jerk for speed 1 + float dx = p2->speedX-p1->speedX; + float dy = p2->speedY-p1->speedY; + float factor=1,tmp; +#if (DRIVE_SYSTEM == 3) // No point computing Z Jerk separately for delta moves + float dz = p2->speedZ-p1->speedZ; + float jerk = sqrt(dx*dx+dy*dy+dz*dz); +#else + float jerk = sqrt(dx*dx+dy*dy); +#endif + //if(DEBUG_ECHO) {OUT_P_F_LN("Jerk:",jerk);OUT_P_F_LN("FS:",p1->fullSpeed);OUT_P_F_LN("MaxJerk:",printer_state.maxJerk);} + if(jerk>printer_state.maxJerk) + factor = printer_state.maxJerk/jerk; +#if (DRIVE_SYSTEM!=3) + if((p1->dir & 64) || (p2->dir & 64)) { + // float dz = (p2->speedZ*p2->invFullSpeed-p1->speedZ*p1->invFullSpeed)*printer_state.maxJerk/printer_state.maxZJerk; + float dz = fabs(p2->speedZ-p1->speedZ); + if(dz>printer_state.maxZJerk) { + tmp = printer_state.maxZJerk/dz; + if(tmpspeedE-p1->speedE); + if(eJerk>current_extruder->maxStartFeedrate) { + tmp = current_extruder->maxStartFeedrate/eJerk; + if(tmpmaxJunctionSpeed = p1->fullSpeed*factor; + if(p1->maxJunctionSpeed>p2->fullSpeed) p1->maxJunctionSpeed = p2->fullSpeed; + //if(DEBUG_ECHO) OUT_P_F_LN("Factor:",factor); + //if(DEBUG_ECHO) OUT_P_F_LN("JSPD:",p1->maxJunctionSpeed); +} + +/** Update parameter used by updateTrapezoids + +Computes the acceleration/decelleration steps and advanced parameter associated. +*/ +void updateStepsParameter(PrintLine *p/*,byte caller*/) { + if(p->flags & FLAG_WARMUP) return; + if(p->joinFlags & FLAG_JOIN_STEPPARAMS_COMPUTED) return; // Already up to date, spare time + float startFactor = p->startSpeed * p->invFullSpeed; + float endFactor = p->endSpeed * p->invFullSpeed; + p->vStart = p->vMax*startFactor; //starting speed + p->vEnd = p->vMax*endFactor; + unsigned long vmax2 = U16SquaredToU32(p->vMax); + p->accelSteps = ((vmax2-U16SquaredToU32(p->vStart))/(p->accelerationPrim<<1))+1; // Always add 1 for missing precision + p->decelSteps = ((vmax2-U16SquaredToU32(p->vEnd)) /(p->accelerationPrim<<1))+1; +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + p->advanceStart = (float)p->advanceFull*startFactor * startFactor; + p->advanceEnd = (float)p->advanceFull*endFactor * endFactor; +#endif +#endif + if(p->accelSteps+p->decelSteps>=p->stepsRemaining) { // can't reach limit speed + unsigned int red = (p->accelSteps+p->decelSteps+2-p->stepsRemaining)>>1; + if(redaccelSteps) + p->accelSteps-=red; + else + p->accelSteps = 0; + if(reddecelSteps) { + p->decelSteps-=red; + } else + p->decelSteps = 0; + } + p->joinFlags|=FLAG_JOIN_STEPPARAMS_COMPUTED; +#ifdef DEBUG_QUEUE_MOVE + if(DEBUG_ECHO) { + out.print_int_P(PSTR("ID:"),(int)p); + out.print_int_P(PSTR("vStart/End:"),p->vStart); + out.println_int_P(PSTR("/"),p->vEnd); + out.print_int_P(PSTR("accel/decel steps:"),p->accelSteps); + out.println_int_P(PSTR("/"),p->decelSteps); + out.print_float_P(PSTR("st./end speed:"),p->startSpeed); + out.println_float_P(PSTR("/"),p->endSpeed); +#if USE_OPS==1 + if(!(p->dir & 128) && printer_state.opsMode==2) + out.println_long_P(PSTR("Reverse at:"),p->opsReverseSteps); +#endif + out.println_int_P(PSTR("flags:"),p->flags); + out.println_int_P(PSTR("joinFlags:"),p->joinFlags); + } +#endif +} +void testnum(float x,char c) { +if (isnan(x)) { + out.print(c); + OUT_P_LN("NAN"); + return; + } + + if (isinf(x)) { + out.print(c); + OUT_P_LN("INF"); + return; + } +} + +/** +Compute the maximum speed from the last entered move. +The backwards planner traverses the moves from last to first looking at deceleration. The RHS of the accelerate/decelerate ramp. + +p = last line inserted +last = last element until we check +*/ +inline void backwardPlanner(byte p,byte last) { + if(p==last) return; + PrintLine *act = &lines[p],*prev; + float lastJunctionSpeed = act->endSpeed; // Start always with safe speed + //PREVIOUS_PLANNER_INDEX(last); // Last element is already fixed in start speed + while(p!=last) { + PREVIOUS_PLANNER_INDEX(p); + prev = &lines[p]; +#if USE_OPS==1 + // Retraction points are fixed points where the extruder movement stops anyway. Finish the computation for the move and exit. + if(printer_state.opsMode && printmoveSeen) { + if((prev->dir & 136)==136 && (act->dir & 136)!=136) { + if((act->dir & 64)!=0 || act->distance>printer_state.opsMinDistance) { // Switch printing - travel + act->joinFlags |= FLAG_JOIN_START_RETRACT | FLAG_JOIN_START_FIXED; // enable retract for this point + prev->joinFlags |= FLAG_JOIN_END_FIXED; + return; + } else { + act->joinFlags |= FLAG_JOIN_NO_RETRACT; + } + } else + if((prev->dir & 136)!=136 && (act->dir & 136)==136) { // Switch travel - print + prev->joinFlags |= FLAG_JOIN_END_RETRACT | FLAG_JOIN_END_FIXED; // reverse retract for this point + if(printer_state.opsMode==2) { + prev->opsReverseSteps = ((long)printer_state.opsPushbackSteps*(long)printer_state.maxExtruderSpeed*TIMER0_PRESCALE)/prev->fullInterval; + long ponr = prev->stepsRemaining/(1.0+0.01*printer_state.opsMoveAfter); + if(prev->opsReverseSteps>ponr) + prev->opsReverseSteps = ponr; + } + act->joinFlags |= FLAG_JOIN_START_FIXED; // Wait only with safe speeds! + return; + } + } +#endif + // Avoid speed calc once crusing in split delta move +#if DRIVE_SYSTEM==3 + if (prev->moveID==act->moveID && lastJunctionSpeed==prev->maxJunctionSpeed) { + act->startSpeed = prev->endSpeed = lastJunctionSpeed; + prev->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + act->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + } +#endif + + // Switch move-retraction or vice versa start always with save speeds! Keeps extruder from blocking + if(((prev->dir & 240)!=128) && ((act->dir & 240)==128)) { // switch move - extruder only move + prev->joinFlags |= FLAG_JOIN_END_FIXED; + act->joinFlags |= FLAG_JOIN_START_FIXED; + return; + } + if(prev->joinFlags & FLAG_JOIN_END_FIXED) { // Nothing to update from here on, happens when path optimize disabled + act->joinFlags |= FLAG_JOIN_START_FIXED; // Wait only with safe speeds! + return; + } + + // Avoid speed calcs if we know we can accelerate within the line + if (act->flags & FLAG_NOMINAL) + lastJunctionSpeed = act->fullSpeed; + else + // If you accelerate from end of move to start what speed to you reach? + lastJunctionSpeed = sqrt(lastJunctionSpeed*lastJunctionSpeed+act->acceleration); // acceleration is acceleration*distance*2! What can be reached if we try? + // If that speed is more that the maximum junction speed allowed then ... + if(lastJunctionSpeed>=prev->maxJunctionSpeed) { // Limit is reached + // If the previous line's end speed has not been updated to maximum speed then do it now + if(prev->endSpeed!=prev->maxJunctionSpeed) { + prev->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + prev->endSpeed = prev->maxJunctionSpeed; // possibly unneeded??? + } + // If actual line start speed has not been updated to maximum speed then do it now + if(act->startSpeed!=prev->maxJunctionSpeed) { + act->startSpeed = prev->maxJunctionSpeed; // possibly unneeded??? + act->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + } + lastJunctionSpeed = prev->maxJunctionSpeed; + } else { + // Block prev end and act start as calculated speed and recalculate plateau speeds (which could move the speed higher again) + act->startSpeed = prev->endSpeed = lastJunctionSpeed; + prev->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + act->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + } + act = prev; + } // while loop +} + +inline void forwardPlanner(byte p) { + PrintLine *act,*next; + if(p==lines_write_pos) return; + byte last = lines_write_pos; + //NEXT_PLANNER_INDEX(last); + next = &lines[p]; + float leftspeed = next->startSpeed; + + while(p!=last) { // All except last segment, which has fixed end speed + act = next; + NEXT_PLANNER_INDEX(p); + next = &lines[p]; + if(act->joinFlags & FLAG_JOIN_END_FIXED) { + leftspeed = act->endSpeed; + continue; // Nothing to do here + } + // Avoid speed calc once crusing in split delta move + #if DRIVE_SYSTEM==3 + if (act->moveID == next->moveID && act->endSpeed == act->maxJunctionSpeed) { + act->startSpeed = leftspeed; + leftspeed = act->endSpeed; + act->joinFlags |= FLAG_JOIN_END_FIXED; + next->joinFlags |= FLAG_JOIN_START_FIXED; + continue; + } + #endif + + + float vmax_right; + // Avoid speed calcs if we know we can accelerate within the line. + if (act->flags & FLAG_NOMINAL) + vmax_right = act->fullSpeed; + else + vmax_right = sqrt(leftspeed*leftspeed+act->acceleration); // acceleration is 2*acceleration*distance!, 1000 Ticks + if(vmax_right>act->endSpeed) { // Could be higher next run? + act->startSpeed = leftspeed; + leftspeed = act->endSpeed; + if(act->endSpeed==act->maxJunctionSpeed) {// Full speed reached, don't compute again! + act->joinFlags |= FLAG_JOIN_END_FIXED; + next->joinFlags |= FLAG_JOIN_START_FIXED; + } + act->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + } else { // We can accelerate full speed without reaching limit, which is as fast as possible. Fix it! + act->joinFlags |= FLAG_JOIN_END_FIXED | FLAG_JOIN_START_FIXED; + act->joinFlags &= ~FLAG_JOIN_STEPPARAMS_COMPUTED; // Needs recomputation + act->startSpeed = leftspeed; + act->endSpeed = next->startSpeed = leftspeed = vmax_right; + next->joinFlags |= FLAG_JOIN_START_FIXED; + } + } + next->startSpeed = leftspeed; // This is the new segment, wgich is updated anyway, no extra flag needed. +} + +/** +This is the path planner. + +It goes from the last entry and tries to increase the end speed of previous moves in a fashion that the maximum jerk +is never exceeded. If a segment with reached maximum speed is met, the planner stops. Everything left from this +is already optimal from previous updates. +The first 2 entries in the queue are not checked. The first is the one that is already in print and the following will likely become active. + +The method is called before lines_count is increased! +*/ +void updateTrapezoids(byte p) { + byte first = p; + PrintLine *firstLine; + PrintLine *act = &lines[p]; + BEGIN_INTERRUPT_PROTECTED; + byte maxfirst = lines_pos; // first non fixed segment + if(maxfirst!=p) + NEXT_PLANNER_INDEX(maxfirst); // don't touch the line printing + // Now ignore enough segments to gain enough time for path planning + long timeleft = 0; + while(timeleft<4500*MOVE_CACHE_SIZE && maxfirst!=p) { + timeleft+=lines[maxfirst].timeInTicks; + NEXT_PLANNER_INDEX(maxfirst); + } + while(first!=maxfirst && !(lines[first].joinFlags & FLAG_JOIN_END_FIXED)) { + PREVIOUS_PLANNER_INDEX(first); + } + if(first!=p && (lines[first].joinFlags & FLAG_JOIN_END_FIXED)) { + NEXT_PLANNER_INDEX(first); + } + // First is now the new element or the first element with non fixed end speed. + // anyhow, the start speed of first is fixed + firstLine = &lines[first]; + firstLine->flags |= FLAG_BLOCKED; // don't let printer touch this or following segments during update + END_INTERRUPT_PROTECTED; + byte previdx = p-1; + if(previdx>=MOVE_CACHE_SIZE) previdx = MOVE_CACHE_SIZE-1; + if(lines_count && (lines[previdx].flags & FLAG_WARMUP)==0) + computeMaxJunctionSpeed(&lines[previdx],act); // Set maximum junction speed if we have a real move before + else + act->joinFlags |= FLAG_JOIN_START_FIXED; + + backwardPlanner(p,first); + // Reduce speed to reachable speeds + forwardPlanner(first); + + // Update precomputed data + do { + updateStepsParameter(&lines[first]); + lines[first].flags &= ~FLAG_BLOCKED; // Flying block to release next used segment as early as possible + NEXT_PLANNER_INDEX(first); + lines[first].flags |= FLAG_BLOCKED; + } while(first!=lines_write_pos); + updateStepsParameter(act); + act->flags &= ~FLAG_BLOCKED; +} + + +// ########################################################################## +// ### Motion computations ### +// ########################################################################## + +inline float safeSpeed(PrintLine *p) { + float safe = min(p->fullSpeed,printer_state.maxJerk*0.5); +#if DRIVE_SYSTEM != 3 + if(p->dir & 64) { + if(fabs(p->speedZ)>printer_state.maxZJerk*0.5) { + float safe2 = printer_state.maxZJerk*0.5*p->fullSpeed/fabs(p->speedZ); + if(safe2dir & 128) { + if(p->dir & 112) { + float safe2 = 0.5*current_extruder->maxStartFeedrate*p->fullSpeed/fabs(p->speedE); + if(safe2maxStartFeedrate; // This is a retraction move + } + } + return (safefullSpeed?safe:p->fullSpeed); +} + +/** +Move printer the given number of steps. Puts the move into the queue. Used by e.g. homing commands. +*/ +void move_steps(long x,long y,long z,long e,float feedrate,bool waitEnd,bool check_endstop) { + float saved_feedrate = printer_state.feedrate; + for(byte i=0; i < 4; i++) { + printer_state.destinationSteps[i] = printer_state.currentPositionSteps[i]; + } + printer_state.destinationSteps[0]+=x; + printer_state.destinationSteps[1]+=y; + printer_state.destinationSteps[2]+=z; + printer_state.destinationSteps[3]+=e; + printer_state.feedrate = feedrate; +#if DRIVE_SYSTEM==3 + split_delta_move(check_endstop,false,false); +#else + queue_move(check_endstop,false); +#endif + printer_state.feedrate = saved_feedrate; + if(waitEnd) + wait_until_end_of_move(); +} + +/** Check if move is new. If it is insert some dummy moves to allow the path optimizer to work since it does +not act on the first two moves in the queue. The stepper timer will spot these moves and leave some time for +processing. +*/ +byte check_new_move(byte pathOptimize, byte waitExtraLines) { + if(lines_count==0 && waitRelax==0 && pathOptimize) { // First line after some time - warmup needed +#ifdef DEBUG_OPS + out.println_P(PSTR("New path")); +#endif + byte w = 3; + PrintLine *p = &lines[lines_write_pos]; + while(w) { + p->flags = FLAG_WARMUP; + p->joinFlags = FLAG_JOIN_STEPPARAMS_COMPUTED | FLAG_JOIN_END_FIXED | FLAG_JOIN_START_FIXED; + p->dir = 0; + p->primaryAxis = w + waitExtraLines; + p->timeInTicks = p->accelerationPrim = p->facceleration = 10000*(unsigned int)w; + lines_write_pos++; + if(lines_write_pos>=MOVE_CACHE_SIZE) lines_write_pos = 0; +BEGIN_INTERRUPT_PROTECTED + lines_count++; +END_INTERRUPT_PROTECTED + p = &lines[lines_write_pos]; + w--; + } + return 1; + } + return 0; +} +void log_long_array(PGM_P ptr,long *arr) { + out.print_P(ptr); + for(byte i=0;i<4;i++) { + out.print(' '); + out.print(arr[i]); + } + out.println(); +} +void log_float_array(PGM_P ptr,float *arr) { + out.print_P(ptr); + for(byte i=0;i<3;i++) + out.print_float_P(PSTR(" "),arr[i]); + out.println_float_P(PSTR(" "),arr[3]); +} +void log_printLine(PrintLine *p) { + out.println_int_P(PSTR("ID:"),(int)p); + log_long_array(PSTR("Delta"),p->delta); + //log_long_array(PSTR("Error"),p->error); + //out.println_int_P(PSTR("Prim:"),p->primaryAxis); + out.println_int_P(PSTR("Dir:"),p->dir); + out.println_int_P(PSTR("Flags:"),p->flags); + out.println_float_P(PSTR("fullSpeed:"),p->fullSpeed); + out.println_long_P(PSTR("vMax:"),p->vMax); + out.println_float_P(PSTR("Acceleration:"),p->acceleration); + out.println_long_P(PSTR("Acceleration Prim:"),p->accelerationPrim); + //out.println_long_P(PSTR("Acceleration Timer:"),p->facceleration); + out.println_long_P(PSTR("Remaining steps:"),p->stepsRemaining); +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + out.println_long_P(PSTR("advanceFull:"),p->advanceFull>>16); + out.println_long_P(PSTR("advanceRate:"),p->advanceRate); +#endif +#endif +} + + +void calculate_move(PrintLine *p,float axis_diff[],byte check_endstops,byte pathOptimize) +{ +#if DRIVE_SYSTEM==3 + long axis_interval[5]; +#else + long axis_interval[4]; +#endif + float time_for_move = (float)(F_CPU)*p->distance / printer_state.feedrate; // time is in ticks + bool critical=false; + if(lines_counttimeInTicks = time_for_move; + UI_MEDIUM; // do check encoder + // Compute the solwest allowed interval (ticks/step), so maximum feedrate is not violated + long limitInterval = time_for_move/p->stepsRemaining; // until not violated by other constraints it is your target speed + axis_interval[0] = fabs(axis_diff[0])*F_CPU/(max_feedrate[0]*p->stepsRemaining); // mm*ticks/s/(mm/s*steps) = ticks/step + if(axis_interval[0]>limitInterval) limitInterval = axis_interval[0]; + axis_interval[1] = fabs(axis_diff[1])*F_CPU/(max_feedrate[1]*p->stepsRemaining); + if(axis_interval[1]>limitInterval) limitInterval = axis_interval[1]; + if(p->dir & 64) { // normally no move in z direction + axis_interval[2] = fabs((float)axis_diff[2])*(float)F_CPU/(float)(max_feedrate[2]*p->stepsRemaining); // must prevent overflow! + if(axis_interval[2]>limitInterval) limitInterval = axis_interval[2]; + } else axis_interval[2] = 0; + axis_interval[3] = fabs(axis_diff[3])*F_CPU/(max_feedrate[3]*p->stepsRemaining); + if(axis_interval[3]>limitInterval) limitInterval = axis_interval[3]; +#if DRIVE_SYSTEM==3 + axis_interval[4] = fabs(axis_diff[4])*F_CPU/(max_feedrate[0]*p->stepsRemaining); +#endif + + p->fullInterval = limitInterval>200 ? limitInterval : 200; // This is our target speed + // new time at full speed = limitInterval*p->stepsRemaining [ticks] + time_for_move = (float)limitInterval*(float)p->stepsRemaining; // for large z-distance this overflows with long computation + float inv_time_s = (float)F_CPU/time_for_move; + if(p->dir & 16) { + axis_interval[0] = time_for_move/p->delta[0]; + p->speedX = axis_diff[0]*inv_time_s; + if(!(p->dir & 1)) p->speedX = -p->speedX; + } else p->speedX = 0; + if(p->dir & 32) { + axis_interval[1] = time_for_move/p->delta[1]; + p->speedY = axis_diff[1]*inv_time_s; + if(!(p->dir & 2)) p->speedY = -p->speedY; + } else p->speedY = 0; + if(p->dir & 64) { + axis_interval[2] = time_for_move/p->delta[2]; + p->speedZ = axis_diff[2]*inv_time_s; + if(!(p->dir & 4)) p->speedZ = -p->speedZ; + } else p->speedZ = 0; + if(p->dir & 128) { + axis_interval[3] = time_for_move/p->delta[3]; + p->speedE = axis_diff[3]*inv_time_s; + if(!(p->dir & 8)) p->speedE = -p->speedE; + } +#if DRIVE_SYSTEM==3 + axis_interval[4] = time_for_move/p->stepsRemaining; +#endif + p->fullSpeed = p->distance*inv_time_s; + + + //long interval = axis_interval[primary_axis]; // time for every step in ticks with full speed + byte is_print_move = (p->dir & 136)==136; // are we printing +#if USE_OPS==1 + if(is_print_move) printmoveSeen = 1; +#endif + //If acceleration is enabled, do some Bresenham calculations depending on which axis will lead it. + #ifdef RAMP_ACCELERATION + + // slowest time to accelerate from v0 to limitInterval determines used acceleration + // t = (v_end-v_start)/a + float slowest_axis_plateau_time_repro = 1e20; // repro to reduce division Unit: 1/s + for(byte i=0; i < 4 ; i++) { + // Errors for delta move are initialized in timer + #if DRIVE_SYSTEM!=3 + p->error[i] = p->delta[p->primaryAxis] >> 1; + #endif + if(p->dir & (16< t = v/a = F_CPU/(c*a) => 1/t = c*a/F_CPU + slowest_axis_plateau_time_repro = min(slowest_axis_plateau_time_repro, + (float)axis_interval[i] * (float)(is_print_move ? axis_steps_per_sqr_second[i] : axis_travel_steps_per_sqr_second[i])); // steps/s^2 * step/tick Ticks/s^2 + } + } + // Errors for delta move are initialized in timer (except extruder) +#if DRIVE_SYSTEM==3 + p->error[3] = p->stepsRemaining >> 1; +#endif + p->invFullSpeed = 1.0/p->fullSpeed; + p->accelerationPrim = slowest_axis_plateau_time_repro / axis_interval[p->primaryAxis]; // a = v/t = F_CPU/(c*t): Steps/s^2 + //Now we can calculate the new primary axis acceleration, so that the slowest axis max acceleration is not violated + p->facceleration = 262144.0*(float)p->accelerationPrim/F_CPU; // will overflow without float! + p->acceleration = 2.0*p->distance*slowest_axis_plateau_time_repro*p->fullSpeed/((float)F_CPU); // mm^2/s^2 + p->startSpeed = p->endSpeed = safeSpeed(p); + // Can accelerate to full speed within the line + if (sqrt(p->startSpeed*p->startSpeed+p->acceleration) >= p->fullSpeed) + p->flags |= FLAG_NOMINAL; + + p->vMax = F_CPU / p->fullInterval; // maximum steps per second, we can reach + // if(p->vMax>46000) // gets overflow in N computation + // p->vMax = 46000; + //p->plateauN = (p->vMax*p->vMax/p->accelerationPrim)>>1; +#ifdef USE_ADVANCE + if((p->dir & 112)==0 || (p->dir & 128)==0 || (p->dir & 8)==0) { +#ifdef ENABLE_QUADRATIC_ADVANCE + p->advanceRate = 0; // No head move or E move only or sucking filament back + p->advanceFull = 0; +#endif + p->advanceL = 0; + } else { + float advlin = fabs(p->speedE)*current_extruder->advanceL*0.001*axis_steps_per_unit[3]; + p->advanceL = (65536*advlin)/p->vMax; //advanceLscaled = (65536*vE*k2)/vMax + #ifdef ENABLE_QUADRATIC_ADVANCE; + p->advanceFull = 65536*current_extruder->advanceK*p->speedE*p->speedE; // Steps*65536 at full speed + long steps = (U16SquaredToU32(p->vMax))/(p->accelerationPrim<<1); // v^2/(2*a) = steps needed to accelerate from 0-vMax + p->advanceRate = p->advanceFull/steps; + if((p->advanceFull>>16)>maxadv) { + maxadv = (p->advanceFull>>16); + maxadvspeed = fabs(p->speedE); + } + #endif + if(advlin>maxadv2) { + maxadv2 = advlin; + maxadvspeed = fabs(p->speedE); + } + } +#endif + UI_MEDIUM; // do check encoder + updateTrapezoids(lines_write_pos); + // how much steps on primary axis do we need to reach target feedrate + //p->plateauSteps = (long) (((float)p->acceleration *0.5f / slowest_axis_plateau_time_repro + p->vMin) *1.01f/slowest_axis_plateau_time_repro); + #else + #ifdef USE_ADVANCE + #ifdef ENABLE_QUADRATIC_ADVANCE + p->advanceRate = 0; // No advance for constant speeds + p->advanceFull = 0; + #endif + #endif + #endif + + // Correct integers for fixed point math used in bresenham_step + if(p->fullIntervalhalfstep = 0; + else { + p->halfstep = 1; +#if DRIVE_SYSTEM==3 + // Error 0-2 are used for the towers and set up in the timer + p->error[3] = p->stepsRemaining; +#else + p->error[0] = p->error[1] = p->error[2] = p->error[3] = p->delta[p->primaryAxis]; +#endif + } +#ifdef DEBUG_STEPCOUNT +// Set in delta move calculation +#if DRIVE_SYSTEM!=3 + p->totalStepsRemaining = p->delta[0]+p->delta[1]+p->delta[2]; +#endif +#endif +#ifdef DEBUG_QUEUE_MOVE + if(DEBUG_ECHO) { + log_printLine(p); + OUT_P_L_LN("limitInterval:", limitInterval); + OUT_P_F_LN("Move distance on the XYZ space:", p->distance); + OUT_P_F_LN("Commanded feedrate:", printer_state.feedrate); + OUT_P_F_LN("Constant full speed move time:", time_for_move); + //log_long_array(PSTR("axis_int"),(long*)axis_interval); + //out.println_float_P(PSTR("Plateau repro:"),slowest_axis_plateau_time_repro); + } +#endif + // Make result permanent + NEXT_PLANNER_INDEX(lines_write_pos); + if (pathOptimize) waitRelax = 70; +BEGIN_INTERRUPT_PROTECTED + lines_count++; +END_INTERRUPT_PROTECTED + DEBUG_MEMORY; +} + +#if DRIVE_SYSTEM != 3 +/** + Put a move to the current destination coordinates into the movement cache. + If the cache is full, the method will wait, until a place gets free. During + wait communication and temperature control is enabled. + @param check_endstops Read endstop during move. +*/ +void queue_move(byte check_endstops,byte pathOptimize) { + printer_state.flag0 &= ~PRINTER_FLAG0_STEPPER_DISABLED; // Motor is enabled now + while(lines_count>=MOVE_CACHE_SIZE) { // wait for a free entry in movement cache + gcode_read_serial(); + check_periodical(); + } + byte newPath=check_new_move(pathOptimize, 0); + PrintLine *p = &lines[lines_write_pos]; + float axis_diff[4]; // Axis movement in mm + if(check_endstops) p->flags = FLAG_CHECK_ENDSTOPS; + else p->flags = 0; + p->joinFlags = 0; + if(!pathOptimize) p->joinFlags = FLAG_JOIN_END_FIXED; + p->dir = 0; +#if min_software_endstop_x == true + if (printer_state.destinationSteps[0] < 0) printer_state.destinationSteps[0] = 0.0; +#endif +#if min_software_endstop_y == true + if (printer_state.destinationSteps[1] < 0) printer_state.destinationSteps[1] = 0.0; +#endif +#if min_software_endstop_z == true + if (printer_state.destinationSteps[2] < 0) printer_state.destinationSteps[2] = 0.0; +#endif + +#if max_software_endstop_x == true + if (printer_state.destinationSteps[0] > printer_state.xMaxSteps) printer_state.destinationSteps[0] = printer_state.xMaxSteps; +#endif +#if max_software_endstop_y == true + if (printer_state.destinationSteps[1] > printer_state.yMaxSteps) printer_state.destinationSteps[1] = printer_state.yMaxSteps; +#endif +#if max_software_endstop_z == true + if (printer_state.destinationSteps[2] > printer_state.zMaxSteps) printer_state.destinationSteps[2] = printer_state.zMaxSteps; +#endif + //Find direction +#if DRIVE_SYSTEM==0 || defined(NEW_XY_GANTRY) + for(byte i=0; i < 4; i++) { + if((p->delta[i]=printer_state.destinationSteps[i]-printer_state.currentPositionSteps[i])>=0) { + p->dir |= 1<delta[i] = -p->delta[i]; + } + if(i==3 && printer_state.extrudeMultiply!=100) { + p->delta[3]=(long)((p->delta[3]*(float)printer_state.extrudeMultiply)*0.01f); + //p->delta[3]=(p->delta[3]*printer_state.extrudeMultiply)/100; + } + axis_diff[i] = p->delta[i]*inv_axis_steps_per_unit[i]; + if(p->delta[i]) p->dir |= 16<delta[2] = printer_state.destinationSteps[2]-printer_state.currentPositionSteps[2]; + p->delta[3] = printer_state.destinationSteps[3]-printer_state.currentPositionSteps[3]; + p->delta[0] = deltax+deltay; + p->delta[1] = deltax-deltay; +#endif +#if DRIVE_SYSTEM==2 + p->delta[2] = printer_state.destinationSteps[2]-printer_state.currentPositionSteps[2]; + p->delta[3] = printer_state.destinationSteps[3]-printer_state.currentPositionSteps[3]; + p->delta[0] = deltay+deltax; + p->delta[1] = deltay-deltax; +#endif + //Find direction + for(byte i=0; i < 4; i++) { + if(p->delta[i]>=0) { + p->dir |= 1<delta[i]*inv_axis_steps_per_unit[i]; + } else { + axis_diff[i] = p->delta[i]*inv_axis_steps_per_unit[i]; + p->delta[i] = -p->delta[i]; + } + if(p->delta[i]) p->dir |= 16<dir & 240)==0) { + if(newPath) { // need to delete dummy elements, otherwise commands can get locked. + lines_count = 0; + lines_pos = lines_write_pos; + } + return; // No steps included + } + byte primary_axis; + float xydist2; +#if USE_OPS==1 + p->opsReverseSteps=0; +#endif +#if ENABLE_BACKLASH_COMPENSATION + if((p->dir & 112) && ((p->dir & 7)^(printer_state.backlashDir & 7)) & (printer_state.backlashDir >> 3)) { // We need to compensate backlash, add a move + while(lines_count>=MOVE_CACHE_SIZE-1) { // wait for a second free entry in movement cache + gcode_read_serial(); + check_periodical(); + } + byte wpos2 = lines_write_pos+1; + if(wpos2>=MOVE_CACHE_SIZE) wpos2 = 0; + PrintLine *p2 = &lines[wpos2]; + memcpy(p2,p,sizeof(PrintLine)); // Move current data to p2 + byte changed = (p->dir & 7)^(printer_state.backlashDir & 7); + float back_diff[4]; // Axis movement in mm + back_diff[3] = 0; + back_diff[0] = (changed & 1 ? (p->dir & 1 ? printer_state.backlashX : -printer_state.backlashX) : 0); + back_diff[1] = (changed & 2 ? (p->dir & 2 ? printer_state.backlashY : -printer_state.backlashY) : 0); + back_diff[2] = (changed & 4 ? (p->dir & 4 ? printer_state.backlashZ : -printer_state.backlashZ) : 0); + p->dir &=7; // x,y and z are already correct + for(byte i=0; i < 4; i++) { + float f = back_diff[i]*axis_steps_per_unit[i]; + p->delta[i] = abs((long)f); + if(p->delta[i]) p->dir |= 16<delta[1] > p->delta[0] && p->delta[1] > p->delta[2]) p->primaryAxis = 1; + else if (p->delta[0] > p->delta[2] ) p->primaryAxis = 0; + else p->primaryAxis = 2; + p->stepsRemaining = p->delta[p->primaryAxis]; + //Feedrate calc based on XYZ travel distance + xydist2 = back_diff[0] * back_diff[0] + back_diff[1] * back_diff[1]; + if(p->dir & 64) { + p->distance = sqrt(xydist2 + back_diff[2] * back_diff[2]); + } else { + p->distance = sqrt(xydist2); + } + printer_state.backlashDir = (printer_state.backlashDir & 56) | (p2->dir & 7); + calculate_move(p,back_diff,false,pathOptimize); + p = p2; // use saved instance for the real move + } +#endif + + //Define variables that are needed for the Bresenham algorithm. Please note that Z is not currently included in the Bresenham algorithm. + if(p->delta[1] > p->delta[0] && p->delta[1] > p->delta[2] && p->delta[1] > p->delta[3]) primary_axis = 1; + else if (p->delta[0] > p->delta[2] && p->delta[0] > p->delta[3]) primary_axis = 0; + else if (p->delta[2] > p->delta[3]) primary_axis = 2; + else primary_axis = 3; + p->primaryAxis = primary_axis; + p->stepsRemaining = p->delta[primary_axis]; + //Feedrate calc based on XYZ travel distance + // TODO - Simplify since Z will always move + if(p->dir & 112) { +#if DRIVE_SYSTEM==0 || defined(NEW_XY_GANTRY) + xydist2 = axis_diff[0] * axis_diff[0] + axis_diff[1] * axis_diff[1]; +#else + float dx,dy; +#if DRIVE_SYSTEM==1 + dx = 0.5*(axis_diff[0]+axis_diff[1]); + dy = axis_diff[0]-dx; +#endif +#if DRIVE_SYSTEM==2 + dy = 0.5*(axis_diff[0]+axis_diff[1]); + dx = axis_diff[0]-dy; +#endif + xydist2 = dx*dx+dy*dy; +#endif + if(p->dir & 64) { + p->distance = sqrt(xydist2 + axis_diff[2] * axis_diff[2]); + } else { + p->distance = sqrt(xydist2); + } + } else if(p->dir & 128) + p->distance = fabs(axis_diff[3]); + else { + return; // no steps to take, we are finished + } + calculate_move(p,axis_diff,check_endstops,pathOptimize); +} +#endif + +#if DRIVE_SYSTEM==3 +#define DEBUG_DELTA_OVERFLOW +/** + Calculate and cache the delta robot positions of the cartesian move in a line. + @return The largest delta axis move in a single segment + @param p The line to examine. +*/ +inline long calculate_delta_segments(PrintLine *p, byte softEndstop) { + + long destination_steps[3], destination_delta_steps[3]; + + for(byte i=0; i < NUM_AXIS - 1; i++) { + // Save current position + destination_steps[i] = printer_state.currentPositionSteps[i]; + } + +// out.println_byte_P(PSTR("Calculate delta segments:"), p->numDeltaSegments); + p->deltaSegmentReadPos = delta_segment_write_pos; +#ifdef DEBUG_STEPCOUNT + p->totalStepsRemaining=0; +#endif + + long max_axis_move = 0; + unsigned int produced_segments = 0; + for (int s = p->numDeltaSegments; s > 0; s--) { + for(byte i=0; i < NUM_AXIS - 1; i++) + destination_steps[i] += (printer_state.destinationSteps[i] - destination_steps[i]) / s; + + // Wait for buffer here + while(delta_segment_count + produced_segments>=DELTA_CACHE_SIZE) { // wait for a free entry in movement cache + gcode_read_serial(); + check_periodical(); + } + + DeltaSegment *d = &segments[delta_segment_write_pos]; + + // Verify that delta calc has a solution + if (calculate_delta(destination_steps, destination_delta_steps)) { + d->dir = 0; + for(byte i=0; i < NUM_AXIS - 1; i++) { + if (softEndstop && destination_delta_steps[i] > printer_state.maxDeltaPositionSteps) + destination_delta_steps[i] = printer_state.maxDeltaPositionSteps; + long delta = destination_delta_steps[i] - printer_state.currentDeltaPositionSteps[i]; +//#ifdef DEBUG_DELTA_CALC +// out.println_long_P(PSTR("dest:"), destination_delta_steps[i]); +// out.println_long_P(PSTR("cur:"), printer_state.currentDeltaPositionSteps[i]); +//#endif + if (delta == 0) { + d->deltaSteps[i] = 0; + } else if (delta > 0) { + d->dir |= 17< 65535) + out.println_long_P(PSTR("Delta overflow:"), delta); + #endif + d->deltaSteps[i] = delta; + } else { + d->dir |= 16< 65535) + out.println_long_P(PSTR("Delta overflow:"), delta); + #endif + d->deltaSteps[i] = -delta; + } + #ifdef DEBUG_STEPCOUNT + p->totalStepsRemaining += d->deltaSteps[i]; + #endif + + if (max_axis_move < d->deltaSteps[i]) max_axis_move = d->deltaSteps[i]; + printer_state.currentDeltaPositionSteps[i] = destination_delta_steps[i]; + } + } else { + // Illegal position - idnore move + out.println_P(PSTR("Invalid delta coordinate - move ignored")); + d->dir = 0; + for(byte i=0; i < NUM_AXIS - 1; i++) { + d->deltaSteps[i]=0; + } + } + // Move to the next segment + delta_segment_write_pos++; if (delta_segment_write_pos >= DELTA_CACHE_SIZE) delta_segment_write_pos=0; + produced_segments++; + } + BEGIN_INTERRUPT_PROTECTED + delta_segment_count+=produced_segments; + END_INTERRUPT_PROTECTED + + #ifdef DEBUG_STEPCOUNT +// out.println_long_P(PSTR("totalStepsRemaining:"), p->totalStepsRemaining); + #endif + return max_axis_move; +} + +/** + Set delta tower positions + @param xaxis X tower position. + @param yaxis Y tower position. + @param zaxis Z tower position. +*/ +inline void set_delta_position(long xaxis, long yaxis, long zaxis) { + printer_state.currentDeltaPositionSteps[0] = xaxis; + printer_state.currentDeltaPositionSteps[1] = yaxis; + printer_state.currentDeltaPositionSteps[2] = zaxis; +} + +/** + Calculate the delta tower position from a cartesian position + @param cartesianPosSteps Array containing cartesian coordinates. + @param deltaPosSteps Result array with tower coordinates. + @returns 1 if cartesian coordinates have a valid delta tower position 0 if not. +*/ +byte calculate_delta(long cartesianPosSteps[], long deltaPosSteps[]) { + long temp; + long opt = DELTA_DIAGONAL_ROD_STEPS_SQUARED - sq(DELTA_TOWER1_Y_STEPS - cartesianPosSteps[Y_AXIS]); + + if ((temp = opt - sq(DELTA_TOWER1_X_STEPS - cartesianPosSteps[X_AXIS])) >= 0) + deltaPosSteps[X_AXIS] = sqrt(temp) + cartesianPosSteps[Z_AXIS]; + else + return 0; + + if ((temp = opt - sq(DELTA_TOWER2_X_STEPS - cartesianPosSteps[X_AXIS])) >= 0) + deltaPosSteps[Y_AXIS] = sqrt(temp) + cartesianPosSteps[Z_AXIS]; + else + return 0; + + if ((temp = DELTA_DIAGONAL_ROD_STEPS_SQUARED + - sq(DELTA_TOWER3_X_STEPS - cartesianPosSteps[X_AXIS]) + - sq(DELTA_TOWER3_Y_STEPS - cartesianPosSteps[Y_AXIS])) >= 0) + deltaPosSteps[Z_AXIS] = sqrt(temp) + cartesianPosSteps[Z_AXIS]; + else + return 0; + + return 1; +} + +inline void calculate_dir_delta(long difference[], byte *dir, long delta[]) { + *dir = 0; + //Find direction + for(byte i=0; i < 4; i++) { + if(difference[i]>=0) { + delta[i] = difference[i]; + *dir |= 1<=MOVE_CACHE_SIZE) { // wait for a free entry in movement cache + gcode_read_serial(); + check_periodical(); + } + byte newPath=check_new_move(pathOptimize, 0); + PrintLine *p = &lines[lines_write_pos]; + float axis_diff[4]; // Axis movement in mm + if(check_endstops) p->flags = FLAG_CHECK_ENDSTOPS; + else p->flags = 0; + p->joinFlags = 0; + if(!pathOptimize) p->joinFlags = FLAG_JOIN_END_FIXED; + p->dir = 0; + //Find direction + for(byte i=0; i< 3; i++) { + p->delta[i] = 0; + axis_diff[i] = 0; + } + axis_diff[3] = e_diff*inv_axis_steps_per_unit[3]; + if (e_diff >= 0) { + p->delta[3] = e_diff; + p->dir = 0x88; + } else { + p->delta[3] = -e_diff; + p->dir = 0x80; + } + if(printer_state.extrudeMultiply!=100) { + //p->delta[3]=(p->delta[3]*printer_state.extrudeMultiply)/100; + p->delta[3]=(long)((p->delta[3]*(float)printer_state.extrudeMultiply)*0.01f); + } + printer_state.currentPositionSteps[3] = printer_state.destinationSteps[3]; + +#if USE_OPS==1 + p->opsReverseSteps=0; +#endif + p->numDeltaSegments = 0; + //Define variables that are needed for the Bresenham algorithm. Please note that Z is not currently included in the Bresenham algorithm. + p->primaryAxis = 3; + p->stepsRemaining = p->delta[3]; + p->distance = fabs(axis_diff[3]); + p->moveID = lastMoveID++; + calculate_move(p,axis_diff,check_endstops,pathOptimize); +} + +/** + Split a line up into a series of lines with at most MAX_DELTA_SEGMENTS_PER_LINE delta segments. + @param check_endstops Check endstops during the move. + @param pathOptimize Run the path optimizer. + @param delta_step_rate delta step rate in segments per second for the move. +*/ +void split_delta_move(byte check_endstops,byte pathOptimize, byte softEndstop) { + if (softEndstop && printer_state.destinationSteps[2] < 0) printer_state.destinationSteps[2] = 0; + long difference[NUM_AXIS]; + float axis_diff[5]; // Axis movement in mm. Virtual axis in 4; + for(byte i=0; i < NUM_AXIS; i++) { + difference[i] = printer_state.destinationSteps[i] - printer_state.currentPositionSteps[i]; + axis_diff[i] = difference[i] * inv_axis_steps_per_unit[i]; + } + printer_state.filamentPrinted+=axis_diff[3]; + +#if max_software_endstop_r == true +// TODO - Implement radius checking +// I'm guessing I need the floats to prevent overflow. This is pretty horrible. +// The NaN checking in the delta calculation routine should be enough +//float a = difference[0] * difference[0] + difference[1] * difference[1]; +//float b = 2 * (difference[0] * printer_state.currentPositionSteps[0] + difference[1] * printer_state.currentPositionSteps[1]); +//float c = printer_state.currentPositionSteps[0] * printer_state.currentPositionSteps[0] + printer_state.currentPositionSteps[1] * printer_state.currentPositionSteps[1] - r * r; +//float disc = b * b - 4 * a * c; +//if (disc >= 0) { +// float t = (-b + (float)sqrt(disc)) / (2 * a); +// printer_state.destinationSteps[0] = (long) printer_state.currentPositionSteps[0] + difference[0] * t; +// printer_state.destinationSteps[1] = (long) printer_state.currentPositionSteps[1] + difference[1] * t; +//} +#endif + + float save_distance; + byte save_dir; + long save_delta[4]; + calculate_dir_delta(difference, &save_dir, save_delta); + if (!calculate_distance(axis_diff, save_dir, &save_distance)) + return; + + if (!(save_dir & 112)) { + queue_E_move(difference[3],check_endstops,pathOptimize); + return; + } + + int segment_count; + int num_lines; + int segments_per_line; + + if (save_dir & 48) { + // Compute number of seconds for move and hence number of segments needed + float seconds = 100 * save_distance / (printer_state.feedrate * printer_state.feedrateMultiply); +#ifdef DEBUG_SPLIT + out.println_float_P(PSTR("Seconds: "), seconds); +#endif + segment_count = max(1, int(((save_dir & 136)==136 ? DELTA_SEGMENTS_PER_SECOND_PRINT : DELTA_SEGMENTS_PER_SECOND_MOVE) * seconds)); + // Now compute the number of lines needed + num_lines = (segment_count + MAX_DELTA_SEGMENTS_PER_LINE - 1)/MAX_DELTA_SEGMENTS_PER_LINE; + // There could be some error here but it doesn't matter since the number of segments will just be reduced slightly + segments_per_line = segment_count / num_lines; + } else { + // Optimize pure Z axis move. Since a pure Z axis move is linear all we have to watch out for is unsigned integer overuns in + // the queued moves; +#ifdef DEBUG_SPLIT + out.println_long_P(PSTR("Z delta: "), save_delta[2]); +#endif + segment_count = (save_delta[2] + (unsigned long)65534) / (unsigned long)65535; + num_lines = (segment_count + MAX_DELTA_SEGMENTS_PER_LINE - 1)/MAX_DELTA_SEGMENTS_PER_LINE; + segments_per_line = segment_count / num_lines; + } + + long start_position[4], fractional_steps[4]; + for (byte i = 0; i < 4; i++) { + start_position[i] = printer_state.currentPositionSteps[i]; + } + +#ifdef DEBUG_SPLIT + out.println_int_P(PSTR("Segments:"), segment_count); + out.println_int_P(PSTR("Num lines:"), num_lines); + out.println_int_P(PSTR("segments_per_line:"), segments_per_line); +#endif + + printer_state.flag0 &= ~PRINTER_FLAG0_STEPPER_DISABLED; // Motor is enabled now + while(lines_count>=MOVE_CACHE_SIZE) { // wait for a free entry in movement cache + gcode_read_serial(); + check_periodical(); + } + + // Insert dummy moves if necessary + // Nead to leave at least one slot open for the first split move + byte newPath=check_new_move(pathOptimize, min(MOVE_CACHE_SIZE-4,num_lines-1)); + + for (int line_number=1; line_number < num_lines + 1; line_number++) { + while(lines_count>=MOVE_CACHE_SIZE) { // wait for a free entry in movement cache + gcode_read_serial(); + check_periodical(); + } + PrintLine *p = &lines[lines_write_pos]; + // Downside a comparison per loop. Upside one less distance calculation and simpler code. + if (num_lines == 1) { + p->numDeltaSegments = segment_count; + p->dir = save_dir; + for (byte i=0; i < 4; i++) { + p->delta[i] = save_delta[i]; + fractional_steps[i] = difference[i]; + } + p->distance = save_distance; + } else { + for (byte i=0; i < 4; i++) { + printer_state.destinationSteps[i] = start_position[i] + (difference[i] * line_number / num_lines); + fractional_steps[i] = printer_state.destinationSteps[i] - printer_state.currentPositionSteps[i]; + axis_diff[i] = fractional_steps[i]*inv_axis_steps_per_unit[i]; + } + calculate_dir_delta(fractional_steps, &p->dir, p->delta); + calculate_distance(axis_diff, p->dir, &p->distance); + } + + p->joinFlags = 0; + p->moveID = lastMoveID; + + // Only set fixed on last segment + if (line_number == num_lines && !pathOptimize) + p->joinFlags = FLAG_JOIN_END_FIXED; + + if(check_endstops) + p->flags = FLAG_CHECK_ENDSTOPS; + else + p->flags = 0; + + p->numDeltaSegments = segments_per_line; + + #if USE_OPS==1 + p->opsReverseSteps=0; + #endif + + long max_delta_step = calculate_delta_segments(p, softEndstop); + + #ifdef DEBUG_SPLIT + out.println_long_P(PSTR("Max DS:"), max_delta_step); + #endif + long virtual_axis_move = max_delta_step * segments_per_line; + if (virtual_axis_move == 0 && p->delta[3] == 0) { + if (num_lines!=1) + OUT_P_LN("ERROR: No move in delta segment with > 1 segment. This should never happen and may cause a problem!"); + return; // Line too short in low precision area + } + p->primaryAxis = 4; // Virtual axis will lead bresenham step either way + if (virtual_axis_move > p->delta[3]) { // Is delta move or E axis leading + p->stepsRemaining = virtual_axis_move; + axis_diff[4] = virtual_axis_move * inv_axis_steps_per_unit[0]; // Steps/unit same as all the towers + // Virtual axis steps per segment + p->numPrimaryStepPerSegment = max_delta_step; + } else { + // Round up the E move to get something divisible by segment count which is greater than E move + p->numPrimaryStepPerSegment = (p->delta[3] + segments_per_line - 1) / segments_per_line; + p->stepsRemaining = p->numPrimaryStepPerSegment * segments_per_line; + axis_diff[4] = p->stepsRemaining * inv_axis_steps_per_unit[0]; + } +#ifdef DEBUG_SPLIT + out.println_long_P(PSTR("Steps Per Segment:"), p->numPrimaryStepPerSegment); + out.println_long_P(PSTR("Virtual axis step:"), p->stepsRemaining); +#endif + + calculate_move(p,axis_diff,check_endstops,pathOptimize); + for (byte i=0; i < 4; i++) { + printer_state.currentPositionSteps[i] += fractional_steps[i]; + } + } + lastMoveID++; // Will wrap at 255 +} + +#endif + +#if ARC_SUPPORT +// Arc function taken from grbl +// The arc is approximated by generating a huge number of tiny, linear segments. The length of each +// segment is configured in settings.mm_per_arc_segment. +void mc_arc(float *position, float *target, float *offset, float radius, uint8_t isclockwise) +{ + // int acceleration_manager_was_enabled = plan_is_acceleration_manager_enabled(); + // plan_set_acceleration_manager_enabled(false); // disable acceleration management for the duration of the arc + float center_axis0 = position[0] + offset[0]; + float center_axis1 = position[1] + offset[1]; + float linear_travel = 0; //target[axis_linear] - position[axis_linear]; + float extruder_travel = printer_state.destinationSteps[3]-printer_state.currentPositionSteps[3]; + float r_axis0 = -offset[0]; // Radius vector from center to current location + float r_axis1 = -offset[1]; + float rt_axis0 = target[0] - center_axis0; + float rt_axis1 = target[1] - center_axis1; + long xtarget = printer_state.destinationSteps[0]; + long ytarget = printer_state.destinationSteps[1]; + long etarget = printer_state.destinationSteps[3]; + + // CCW angle between position and target from circle center. Only one atan2() trig computation required. + float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); + if (angular_travel < 0) { angular_travel += 2*M_PI; } + if (isclockwise) { angular_travel -= 2*M_PI; } + + float millimeters_of_travel = fabs(angular_travel)*radius; //hypot(angular_travel*radius, fabs(linear_travel)); + if (millimeters_of_travel < 0.001) { return; } + //uint16_t segments = (radius>=BIG_ARC_RADIUS ? floor(millimeters_of_travel/MM_PER_ARC_SEGMENT_BIG) : floor(millimeters_of_travel/MM_PER_ARC_SEGMENT)); + // Increase segment size if printing faster then computation speed allows + uint16_t segments = (printer_state.feedrate>60 ? floor(millimeters_of_travel/min(MM_PER_ARC_SEGMENT_BIG,printer_state.feedrate*0.01666*MM_PER_ARC_SEGMENT)) : floor(millimeters_of_travel/MM_PER_ARC_SEGMENT)); + if(segments == 0) segments = 1; + /* + // Multiply inverse feed_rate to compensate for the fact that this movement is approximated + // by a number of discrete segments. The inverse feed_rate should be correct for the sum of + // all segments. + if (invert_feed_rate) { feed_rate *= segments; } + */ + float theta_per_segment = angular_travel/segments; + float linear_per_segment = linear_travel/segments; + float extruder_per_segment = extruder_travel/segments; + + /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, + and phi is the angle of rotation. Based on the solution approach by Jens Geisler. + r_T = [cos(phi) -sin(phi); + sin(phi) cos(phi] * r ; + + For arc generation, the center of the circle is the axis of rotation and the radius vector is + defined from the circle center to the initial position. Each line segment is formed by successive + vector rotations. This requires only two cos() and sin() computations to form the rotation + matrix for the duration of the entire arc. Error may accumulate from numerical round-off, since + all double numbers are single precision on the Arduino. (True double precision will not have + round off issues for CNC applications.) Single precision error can accumulate to be greater than + tool precision in some cases. Therefore, arc path correction is implemented. + + Small angle approximation may be used to reduce computation overhead further. This approximation + holds for everything, but very small circles and large mm_per_arc_segment values. In other words, + theta_per_segment would need to be greater than 0.1 rad and N_ARC_CORRECTION would need to be large + to cause an appreciable drift error. N_ARC_CORRECTION~=25 is more than small enough to correct for + numerical drift error. N_ARC_CORRECTION may be on the order a hundred(s) before error becomes an + issue for CNC machines with the single precision Arduino calculations. + + This approximation also allows mc_arc to immediately insert a line segment into the planner + without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied + a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead. + This is important when there are successive arc motions. + */ + // Vector rotation matrix values + float cos_T = 1-0.5*theta_per_segment*theta_per_segment; // Small angle approximation + float sin_T = theta_per_segment; + + float arc_target[4]; + float sin_Ti; + float cos_Ti; + float r_axisi; + uint16_t i; + int8_t count = 0; + + // Initialize the linear axis + //arc_target[axis_linear] = position[axis_linear]; + + // Initialize the extruder axis + arc_target[3] = printer_state.currentPositionSteps[3]; + + for (i = 1; i Boards' menu. +#endif + +#define X_STEP_PIN 15 +#define X_DIR_PIN 18 +#define X_ENABLE_PIN 19 +#define X_MIN_PIN 20 +#define X_MAX_PIN 21 + +#define Y_STEP_PIN 23 +#define Y_DIR_PIN 22 +#define Y_ENABLE_PIN 19 +#define Y_MIN_PIN 25 +#define Y_MAX_PIN 26 + +#define Z_STEP_PIN 29 +#define Z_DIR_PIN 30 +#define Z_ENABLE_PIN 31 +#define Z_MIN_PIN 2 +#define Z_MAX_PIN 1 + +#define E0_STEP_PIN 12 +#define E0_DIR_PIN 16 +#define E0_ENABLE_PIN 3 + +#define SDPOWER -1 +#define SDSS -1 +#define LED_PIN 0 +#define FAN_PIN -1 +#define PS_ON_PIN -1 +#define KILL_PIN -1 + +#define HEATER_0_PIN 14 +#define TEMP_0_PIN 4 //D27 // MUST USE ANALOG INPUT NUMBERING NOT DIGITAL OUTPUT NUMBERING!!!!!!!!! + +/* Unused (1) (2) (3) 4 5 6 7 8 9 10 11 12 13 (14) (15) (16) 17 (18) (19) (20) (21) (22) (23) 24 (25) (26) (27) 28 (29) (30) (31) */ + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS + + +#endif + + +/**************************************************************************************** +* RepRap Motherboard ****---NOOOOOO RS485/EXTRUDER CONTROLLER!!!!!!!!!!!!!!!!!---******* +* +****************************************************************************************/ +#if MOTHERBOARD == 2 +#define KNOWN_BOARD 1 + +#ifndef __AVR_ATmega644P__ +#error Oops! Make sure you have 'Sanguino' selected from the 'Tools -> Boards' menu. +#endif + +#define X_STEP_PIN 15 +#define X_DIR_PIN 18 +#define X_ENABLE_PIN 19 +#define X_MIN_PIN 20 +#define X_MAX_PIN 21 + +#define Y_STEP_PIN 23 +#define Y_DIR_PIN 22 +#define Y_ENABLE_PIN 24 +#define Y_MIN_PIN 25 +#define Y_MAX_PIN 26 + +#define Z_STEP_PINN 27 +#define Z_DIR_PINN 28 +#define Z_ENABLE_PIN 29 +#define Z_MIN_PIN 30 +#define Z_MAX_PIN 31 + +#define E0_STEP_PIN 17 +#define E0_DIR_PIN 16 +#define E0_ENABLE_PIN -1 + +#define SDPOWER -1 +#define SDSS 4 +#define LED_PIN 0 + +#define SD_CARD_WRITE 2 +#define SD_CARD_DETECT 3 +#define SD_CARD_SELECT 4 + +//our RS485 pins +#define TX_ENABLE_PIN 12 +#define RX_ENABLE_PIN 13 + +//pin for controlling the PSU. +#define PS_ON_PIN 14 + +#define FAN_PIN -1 +#define KILL_PIN -1 + +#define HEATER_0_PIN -1 +#define TEMP_0_PIN -1 // MUST USE ANALOG INPUT NUMBERING NOT DIGITAL OUTPUT NUMBERING!!!!!!!!! + + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS + + +#endif + +/**************************************************************************************** +* Arduino Mega pin assignment +* +****************************************************************************************/ +#if MOTHERBOARD == 33 + #define MOTHERBOARD 3 + #define RAMPS_V_1_3 +#endif +#if MOTHERBOARD == 3 + #define KNOWN_BOARD 1 + +//////////////////FIX THIS////////////// + #ifndef __AVR_ATmega1280__ + #ifndef __AVR_ATmega2560__ + #error Oops! Make sure you have 'Arduino Mega' selected from the 'Tools -> Boards' menu. + #endif + #endif + +// uncomment one of the following lines for RAMPS v1.3 or v1.0, comment both for v1.2 or 1.1 +// #define RAMPS_V_1_3 +// #define RAMPS_V_1_0 + +#ifdef RAMPS_V_1_3 + +#define X_STEP_PIN 54 +#define X_DIR_PIN 55 +#define X_ENABLE_PIN 38 +#define X_MIN_PIN 3 +#define X_MAX_PIN 2 + +#define Y_STEP_PIN 60 +#define Y_DIR_PIN 61 +#define Y_ENABLE_PIN 56 +#define Y_MIN_PIN 14 +#define Y_MAX_PIN 15 + +#define Z_STEP_PIN 46 +#define Z_DIR_PIN 48 +#define Z_ENABLE_PIN 62 +#define Z_MIN_PIN 18 +#define Z_MAX_PIN 19 + +#define E0_STEP_PIN 26 +#define E0_DIR_PIN 28 +#define E0_ENABLE_PIN 24 + +#define E1_STEP_PIN 36 +#define E1_DIR_PIN 34 +#define E1_ENABLE_PIN 30 + +#define SDPOWER -1 +#define SDSS 53 +#define SDCARDDETECT 49 + +#define LED_PIN 13 +#define FAN_PIN 9 +#define PS_ON_PIN 12 +#define KILL_PIN -1 + +#define HEATER_0_PIN 10 +#define HEATER_1_PIN 8 +#define TEMP_0_PIN 13 // ANALOG NUMBERING +#define TEMP_1_PIN 14 // ANALOG NUMBERING +#define TEMP_2_PIN 15 +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS E1_STEP_PIN,E1_DIR_PIN,E1_ENABLE_PIN, + + +#else // RAMPS_V_1_1 or RAMPS_V_1_2 as default + +#define X_STEP_PIN 26 +#define X_DIR_PIN 28 +#define X_ENABLE_PIN 24 +#define X_MIN_PIN 3 +#define X_MAX_PIN -1 //2 + +#define Y_STEP_PIN 38 +#define Y_DIR_PIN 40 +#define Y_ENABLE_PIN 36 +#define Y_MIN_PIN 16 +#define Y_MAX_PIN -1 //17 + +#define Z_STEP_PIN 44 +#define Z_DIR_PIN 46 +#define Z_ENABLE_PIN 42 +#define Z_MIN_PIN 18 +#define Z_MAX_PIN -1 //19 + +#define E0_STEP_PIN 32 +#define E0_DIR_PIN 34 +#define E0_ENABLE_PIN 30 + +#define SDPOWER 48 +#define SDSS 53 +#define LED_PIN 13 +#define PS_ON_PIN -1 +#define KILL_PIN -1 +//#define SCL 21 +//#define SDA 20 + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS + + +#ifdef RAMPS_V_1_0 // RAMPS_V_1_0 + #define HEATER_0_PIN 12 // RAMPS 1.0 + #define HEATER_1_PIN -1 // RAMPS 1.0 + #define FAN_PIN 11 // RAMPS 1.0 + +#else // RAMPS_V_1_1 or RAMPS_V_1_2 + #define HEATER_0_PIN 10 // RAMPS 1.1 + #define HEATER_1_PIN 8 // RAMPS 1.1 + #define FAN_PIN 9 // RAMPS 1.1 +#endif + +#define TEMP_0_PIN 2 // MUST USE ANALOG INPUT NUMBERING NOT DIGITAL OUTPUT NUMBERING!!!!!!!!! +#define TEMP_1_PIN 1 // MUST USE ANALOG INPUT NUMBERING NOT DIGITAL OUTPUT NUMBERING!!!!!!!!! +#endif + +// SPI for Max6675 Thermocouple + +// these pins are defined in the SD library if building with SD support +#define SCK_PIN 52 +#define MISO_PIN 50 +#define MOSI_PIN 51 +#define MAX6675_SS 53 + + +#endif + +/**************************************************************************************** +* RUMBA pin assignment +* +****************************************************************************************/ +#if MOTHERBOARD == 80 +#define KNOWN_BOARD 1 + +#ifndef __AVR_ATmega2560__ + #error Oops! Make sure you have 'Arduino Mega' selected from the 'Tools -> Boards' menu. +#endif + +#define X_STEP_PIN 17 +#define X_DIR_PIN 16 +#define X_ENABLE_PIN 48 +#define X_MIN_PIN 37 +#define X_MAX_PIN 36 //Max endstops default to disabled "-1" + +#define Y_STEP_PIN 54 +#define Y_DIR_PIN 47 +#define Y_ENABLE_PIN 55 +#define Y_MIN_PIN 35 +#define Y_MAX_PIN 34 + +#define Z_STEP_PIN 57 +#define Z_DIR_PIN 56 +#define Z_ENABLE_PIN 62 +#define Z_MIN_PIN 33 +#define Z_MAX_PIN 32 + +#define E0_STEP_PIN 23 +#define E0_DIR_PIN 22 +#define E0_ENABLE_PIN 24 + +#define E1_STEP_PIN 26 +#define E1_DIR_PIN 25 +#define E1_ENABLE_PIN 27 + +#define E2_STEP_PIN 29 +#define E2_DIR_PIN 28 +#define E2_ENABLE_PIN 39 + +#define LED_PIN 13 + +#define FAN_PIN 7 +//additional FAN1 PIN (e.g. useful for electronics fan or light on/off) on PIN 8 + +#define PS_ON_PIN 45 +#define KILL_PIN 46 + +#define HEATER_0_PIN 2 // EXTRUDER 1 +#define HEATER_1_PIN 3 // EXTRUDER 2 +#define HEATER_2_PIN 6 // EXTRUDER 3 +//optional FAN1 can be used as 4th heater output: #define HEATER_3_PIN 8 // EXTRUDER 4 +#define HEATER_BED_PIN 9 // BED + +#define TEMP_0_PIN 15 // ANALOG NUMBERING +#define TEMP_1_PIN 14 // ANALOG NUMBERING +#define TEMP_2_PIN 13 // ANALOG NUMBERING +//optional for extruder 4 or chamber: #define TEMP_2_PIN 12 // ANALOG NUMBERING +#define TEMP_BED_PIN 11 // ANALOG NUMBERING + +#define SDPOWER -1 +#define SDSS 53 +#define SCK_PIN 52 +#define MISO_PIN 50 +#define MOSI_PIN 51 + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS E1_STEP_PIN,E1_DIR_PIN,E1_ENABLE_PIN, +#define E2_PINS E2_STEP_PIN,E2_DIR_PIN,E2_ENABLE_PIN, + +#endif //MOTHERBOARD==80 + +/**************************************************************************************** +* Duemilanove w/ ATMega328P pin assignment +* +****************************************************************************************/ +#if MOTHERBOARD == 4 +#define KNOWN_BOARD 1 + +#ifndef __AVR_ATmega328P__ +#error Oops! Make sure you have 'Arduino Duemilanove w/ ATMega328' selected from the 'Tools -> Boards' menu. +#endif + +#define X_STEP_PIN 19 +#define X_DIR_PIN 18 +#define X_ENABLE_PIN -1 +#define X_MIN_PIN 17 +#define X_MAX_PIN -1 + +#define Y_STEP_PIN 10 +#define Y_DIR_PIN 7 +#define Y_ENABLE_PIN -1 +#define Y_MIN_PIN 8 +#define Y_MAX_PIN -1 + +#define Z_STEP_PIN 13 +#define Z_DIR_PIN 3 +#define Z_ENABLE_PIN 2 +#define Z_MIN_PIN 4 +#define Z_MAX_PIN -1 + +#define E0_STEP_PIN 11 +#define E0_DIR_PIN 12 +#define E0_ENABLE_PIN -1 + +#define SDPOWER -1 +#define SDSS -1 +#define LED_PIN -1 +#define FAN_PIN 5 +#define PS_ON_PIN -1 +#define KILL_PIN -1 + +#define HEATER_0_PIN 6 +#define TEMP_0_PIN 0 // MUST USE ANALOG INPUT NUMBERING NOT DIGITAL OUTPUT NUMBERING!!!!!!!!! +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS + + +#endif + +/**************************************************************************************** +* Gen6 pin assignment (5) and Gen6 deluxe assignment (51) +* +****************************************************************************************/ +#if MOTHERBOARD == 5 || MOTHERBOARD == 51 + #define KNOWN_BOARD 1 + +#if !defined(__AVR_ATmega644P__) && !defined(__AVR_ATmega1284P__) + #error Oops! Make sure you have 'Sanguino' selected from the 'Tools -> Boards' menu. +#endif + +//x axis pins + #define X_STEP_PIN 15 + #define X_DIR_PIN 18 + #define X_ENABLE_PIN 19 + #define X_MIN_PIN 20 + #define X_MAX_PIN -1 + + //y axis pins + #define Y_STEP_PIN 23 + #define Y_DIR_PIN 22 + #define Y_ENABLE_PIN 24 + #define Y_MIN_PIN 25 + #define Y_MAX_PIN -1 + + //z axis pins + #define Z_STEP_PIN 27 + #define Z_DIR_PIN 28 + #define Z_ENABLE_PIN 29 + #define Z_MIN_PIN 30 + #define Z_MAX_PIN -1 + + //extruder pins + #define E0_STEP_PIN 4 //Edited @ EJE Electronics 20100715 + #define E0_DIR_PIN 2 //Edited @ EJE Electronics 20100715 + #define E0_ENABLE_PIN 3 //Added @ EJE Electronics 20100715 + #define TEMP_0_PIN 5 //changed @ rkoeppl 20110410 + #define HEATER_0_PIN 14 //changed @ rkoeppl 20110410 +#if MOTHERBOARD == 5 + #define HEATER_1_PIN -1 //changed @ rkoeppl 20110410 + #define TEMP_1_PIN -1 //changed @ rkoeppl 20110410 +#else + #define HEATER_1_PIN 1 //changed @ rkoeppl 20110410 + #define TEMP_1_PIN 0 //changed @ rkoeppl 20110410 +#endif + + + #define SDPOWER -1 + #define SDSS 16 // SCL pin of I2C header + #define LED_PIN -1 //changed @ rkoeppl 20110410 + #define TEMP_1_PIN -1 //changed @ rkoeppl 20110410 + #define FAN_PIN -1 //changed @ rkoeppl 20110410 + #define PS_ON_PIN -1 //changed @ rkoeppl 20110410 + //our pin for debugging. + + #define DEBUG_PIN 0 + + //our RS485 pins + #define TX_ENABLE_PIN 12 + #define RX_ENABLE_PIN 13 + #define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, + #define E1_PINS + + #define SCK_PIN 7 + #define MISO_PIN 6 + #define MOSI_PIN 5 + + // #define SCL 16 + // #define SDA 17 + +#endif +/**************************************************************************************** +* Sanguinololu pin assignment +* +****************************************************************************************/ +#if MOTHERBOARD == 62 + #define MOTHERBOARD 6 + #define SANGUINOLOLU_V_1_2 +#endif +#if MOTHERBOARD == 6 + #define KNOWN_BOARD 1 + //#ifndef __AVR_ATmega644P__ + #if !defined(__AVR_ATmega644P__) && !defined(__AVR_ATmega1284P__) + #error Oops! Make sure you have 'Sanguino' selected from the 'Tools -> Boards' menu. + #endif + + #define X_STEP_PIN 15 + #define X_DIR_PIN 21 + #define X_MIN_PIN 18 + #define X_MAX_PIN -2 + + #define Y_STEP_PIN 22 + #define Y_DIR_PIN 23 + #define Y_MIN_PIN 19 + #define Y_MAX_PIN -1 + + #define Z_STEP_PIN 3 + #define Z_DIR_PIN 2 + #define Z_MIN_PIN 20 + #define Z_MAX_PIN -1 + + #define E0_STEP_PIN 1 + #define E0_DIR_PIN 0 + + #define LED_PIN -1 + + #define FAN_PIN -1 + + #define PS_ON_PIN -1 + #define KILL_PIN -1 + + #define HEATER_0_PIN 13 // (extruder) + + #ifdef SANGUINOLOLU_V_1_2 + + #define HEATER_1_PIN 12 // (bed) + #define X_ENABLE_PIN 14 + #define Y_ENABLE_PIN 14 + #define Z_ENABLE_PIN 26 + #define E0_ENABLE_PIN 14 + + #else + + #define HEATER_1_PIN 14 // (bed) + #define X_ENABLE_PIN -1 + #define Y_ENABLE_PIN -1 + #define Z_ENABLE_PIN -1 + #define E0_ENABLE_PIN -1 + + #endif + + #define TEMP_0_PIN 7 // MUST USE ANALOG INPUT NUMBERING NOT DIGITAL OUTPUT NUMBERING!!!!!!!!! (pin 33 extruder) + #define TEMP_1_PIN 6 // MUST USE ANALOG INPUT NUMBERING NOT DIGITAL OUTPUT NUMBERING!!!!!!!!! (pin 34 bed) + #define SDPOWER -1 + #define SDSS 31 + + #define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, + #define E1_PINS + +#ifndef KNOWN_BOARD +#error Unknown MOTHERBOARD value in configuration.h +#endif + +#endif + +/**************************************************************************************** +* Gen7 1.1 and above pin assignment +* +****************************************************************************************/ +#if MOTHERBOARD == 7 +#define KNOWN_BOARD 1 + +#if !defined(__AVR_ATmega644P__) && !defined(__AVR_ATmega644__) && !defined(__AVR_ATmega1284P__) + #error Oops! Make sure you have 'Gen7' selected from the 'Tools -> Boards' menu. +#endif + +//x axis pins +#define X_STEP_PIN 19 +#define X_DIR_PIN 18 +#define X_ENABLE_PIN 24 +#define X_MIN_PIN 7 +#define X_MAX_PIN -1 + +//y axis pins +#define Y_STEP_PIN 23 +#define Y_DIR_PIN 22 +#define Y_ENABLE_PIN 24 +#define Y_MIN_PIN 5 +#define Y_MAX_PIN -1 + +//z axis pins +#define Z_STEP_PIN 26 +#define Z_DIR_PIN 25 +#define Z_ENABLE_PIN 24 +#define Z_MIN_PIN 1 +#define Z_MAX_PIN -1 + +//extruder pins +#define E0_STEP_PIN 28 +#define E0_DIR_PIN 27 +#define E0_ENABLE_PIN 24 +#define TEMP_0_PIN 1 +#define TEMP_1_PIN 2 +#define HEATER_0_PIN 4 +#define HEATER_1_PIN 3 + + +#define SDPOWER -1 +#define SDSS -1 // SCL pin of I2C header +#define LED_PIN -1 + +#define FAN_PIN 31 +#define PS_ON_PIN 15 +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS + + //our pin for debugging. + + #define DEBUG_PIN 0 + + //our RS485 pins + #define TX_ENABLE_PIN 12 + #define RX_ENABLE_PIN 13 + +#define SCK_PIN 7 +#define MISO_PIN 6 +#define MOSI_PIN 5 + +#endif +/**************************************************************************************** +* Gen7 1.4.1 pin assignment +* +****************************************************************************************/ +#if MOTHERBOARD == 71 +#define KNOWN_BOARD 1 + +#if !defined(__AVR_ATmega644P__) && !defined(__AVR_ATmega644__) && !defined(__AVR_ATmega1284P__) + #error Oops! Make sure you have 'Gen7' selected from the 'Tools -> Boards' menu. +#endif + +//x axis pins + #define X_STEP_PIN 29 + #define X_DIR_PIN 28 + #define X_ENABLE_PIN 25 + #define X_MIN_PIN 0 + #define X_MAX_PIN -1 + + //y axis pins + #define Y_STEP_PIN 27 + #define Y_DIR_PIN 26 + #define Y_ENABLE_PIN 25 + #define Y_MIN_PIN 1 + #define Y_MAX_PIN -1 + + //z axis pins + #define Z_STEP_PIN 23 + #define Z_DIR_PIN 22 + #define Z_ENABLE_PIN 25 + #define Z_MIN_PIN 2 + #define Z_MAX_PIN -1 + + //extruder pins + #define E0_STEP_PIN 19 + #define E0_DIR_PIN 18 + #define E0_ENABLE_PIN 25 + #define TEMP_0_PIN 0 + #define TEMP_1_PIN 1 + #define HEATER_0_PIN 4 + #define HEATER_1_PIN 3 + + + #define SDPOWER -1 + #define SDSS -1 + #define LED_PIN -1 + + #define FAN_PIN -1 + #define PS_ON_PIN 15 + //our pin for debugging. + + #define DEBUG_PIN 0 + + //our RS485 pins + #define TX_ENABLE_PIN 12 + #define RX_ENABLE_PIN 13 + + #define SDPOWER -1 + #define SDSS -1 + + #define SCK_PIN 7 + #define MISO_PIN 6 + #define MOSI_PIN 5 + #define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, + #define E1_PINS +#endif + +/**************************************************************************************** +* Teensylu 0.7 pin assingments (ATMEGA90USB) +* Requires the Teensyduino software with Teensy2.0++ selected in arduino IDE! +****************************************************************************************/ +#if MOTHERBOARD == 8 +#define MOTHERBOARD 8 +#define KNOWN_BOARD 1 + +#define X_STEP_PIN 28 +#define X_DIR_PIN 29 +#define X_ENABLE_PIN 19 +#define X_MIN_PIN 25 +#define X_MAX_PIN -1 + +#define Y_STEP_PIN 30 +#define Y_DIR_PIN 31 +#define Y_ENABLE_PIN 20 //26 +#define Y_MIN_PIN 26 // 20 +#define Y_MAX_PIN -1 + +#define Z_STEP_PIN 32 +#define Z_DIR_PIN 33 +#define Z_ENABLE_PIN 17 +#define Z_MIN_PIN 27 +#define Z_MAX_PIN -1 + +#define E0_STEP_PIN 34 +#define E0_DIR_PIN 35 +#define E0_ENABLE_PIN 13 + +#define TEMP_0_PIN 7 // Extruder - ANALOG PIN NUMBER! +#define TEMP_1_PIN 6 // Bed - ANALOG PIN NUMBER! +#define HEATER_0_PIN 15 // Extruder +#define HEATER_1_PIN 14 // bed + +#define SDPOWER -1 +#define SDSS 20 +#define LED_PIN -1 + +#define FAN_PIN 16 // Fan +#define PS_ON_PIN -1 + +#define KILL_PIN -1 +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS + +#ifndef SDSUPPORT +// these pins are defined in the SD library if building with SD support + #define SCK_PIN 21 + #define MISO_PIN 22 + #define MOSI_PIN 23 +#endif + +#endif + + +/**************************************************************************************** +* Printrboard Rev. B pin assingments (ATMEGA90USB1286) +* Requires the Teensyduino software with Teensy2.0++ selected in arduino IDE! +* See http://reprap.org/wiki/Printrboard for more info +****************************************************************************************/ +#if MOTHERBOARD == 9 +#define MOTHERBOARD 9 +#define KNOWN_BOARD 1 + +#define X_STEP_PIN 28 +#define X_DIR_PIN 29 +#define X_ENABLE_PIN 19 +#define X_MIN_PIN 47 +#define X_MAX_PIN -1 + +#define Y_STEP_PIN 30 +#define Y_DIR_PIN 31 +#define Y_ENABLE_PIN 18 +#define Y_MIN_PIN 20 // Don't use this if you want to use SD card. Use 37 and put the endstop in the e-stop slot!!! +#define Y_MAX_PIN -1 + +#define Z_STEP_PIN 32 +#define Z_DIR_PIN 33 +#define Z_ENABLE_PIN 17 +#define Z_MIN_PIN 36 +#define Z_MAX_PIN -1 + +#define E0_STEP_PIN 34 +#define E0_DIR_PIN 35 +#define E0_ENABLE_PIN 13 +#define TEMP_0_PIN 1 // Extruder - ANALOG PIN NUMBER! +#define TEMP_1_PIN 0 // Bed - ANALOG PIN NUMBER! +#define HEATER_0_PIN 15 // Extruder +#define HEATER_1_PIN 14 // bed + +#define SDPOWER -1 +#define SDSS 26 // old value 2 +#define LED_PIN -1 + +#define FAN_PIN 16 // Fan +#define PS_ON_PIN -1 + +#define KILL_PIN -1 +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS +#ifndef SDSUPPORT +// these pins are defined in the SD library if building with SD support + #define SCK_PIN 21 + #define MISO_PIN 22 + #define MOSI_PIN 23 +#endif + +#endif + +/**************************************************************************************** +* 3D Master pin assignment +* +****************************************************************************************/ +#if MOTHERBOARD == 12 + #define KNOWN_BOARD 1 + + #if !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) + #error Oops! Make sure you have 'Arduino Mega' selected from the 'Tools -> Boards' menu. + #endif + +// Definition for current control +#define STEPPER_CURRENT_CONTROL CURRENT_CONTROL_LTC2600 +#define LTC2600_CHANNELS {0x30,0x31,0x32,0x33,0x34} +#define LTC2600_NUM_CHANNELS 5 +#define LTC2600_CS_PIN 92 // PIND.4, 47, DA_CS +#define LTC2600_SCK_PIN 93 // PIND.5, 48, DA_SCK +#define LTC2600_SDI_PIN 94 // PIND.6, 49, DA_SDI + +// On board beeper, so define values already here +#define BEEPER_PIN 23 +#define BEEPER_TYPE 1 +#define SDSUPPORT true // sd card reader on board +#define SDCARDDETECT -1 + +// digital pin mappings +#define X_STEP_PIN 54 // PINF.0, 97, STP_DRV1 +#define X_DIR_PIN 55 // PINF.1, 96, DIR_DRV1 +#define X_ENABLE_PIN 38 // PIND.7, 50, ENA_DRV1 +#define X_MIN_PIN 3 // PINE.5, 7, OPTO1 +#define X_MAX_PIN -1 // PINJ.0, 63, OPTO4 (would be "15", -1 = disabled) + +#define Y_STEP_PIN 60 // PINF.6, 91, STP_DRV2 +#define Y_DIR_PIN 61 // PINF.7, 90, DIR_DRV2 +#define Y_ENABLE_PIN 56 // PINF.2, 95, ENA_DRV2 +#define Y_MIN_PIN 2 // PINE.4, 6, OPTO2 +#define Y_MAX_PIN -1 // PIND.3, 46, OPTO5 (would be "18", -1 = disabled + +#define Z_STEP_PIN 46 // PINL.3, 38, STP_DRV3 +#define Z_DIR_PIN 48 // PINL.1, 36, DIR_DRV3 +#define Z_ENABLE_PIN 62 // PINK.0, 89, ENA_DRV3 +#define Z_MIN_PIN 14 // PINJ.1, 64, OPTO3 +#define Z_MAX_PIN -1 // PIND.2, 45, OPTO6 (would be "19", -1 = disabled) + +#define E0_STEP_PIN 26 // PINA.4, 74, STP_DRV4 +#define E0_DIR_PIN 28 // PINA.6, 72, DIR_DRV4 +#define E0_ENABLE_PIN 24 // PINA.2, 76 ENA_DRV4 + +#define E1_STEP_PIN 36 // PINC.1, 54, STP_DRV5 +#define E1_DIR_PIN 34 // PINC.3, 56, DIR_DRV5 +#define E1_ENABLE_PIN 30 // PINC.7, 60, ENA_DRV5 + +#define SDPOWER -1 +#define SDSS 53 // PINB.0, 19, SS +#define LED_PIN 13 // PINB.7, 26, LED13 +#define FAN_PIN 25 // OUT1 PINA.3, 75, OUT1 +#define FAN_BOARD_PIN 27 // OUT2 +#define PS_ON_PIN -1 +#define KILL_PIN -1 + +#define HEATER_0_PIN 10 // PINB.4, 23, HZ1 +#define HEATER_1_PIN 9 // PINH.6, 18, HZ2 +#define HEATER_2_PIN 8 // PINH.5, 17, HZ3 + +// analog pin mappings +#define TEMP_0_PIN 13 // PINK.5, 84, TH1 +#define TEMP_1_PIN 14 // PINK.6, 83, TH2 +#define TEMP_2_PIN 15 // PINK.7, 82, TH3 + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS E1_STEP_PIN,E1_DIR_PIN,E1_ENABLE_PIN, + +// these pins are defined in the SD library if building with SD support +#define SCK_PIN 52 // PINB.1, 20, SCK +#define MISO_PIN 50 // PINB.3, 22, MISO +#define MOSI_PIN 51 // PINB.2, 21, MOSI +#define MAX6675_SS 53 // PINB.0, 19, SS + +#endif // MOTHERBOARD == 12 + + +/**************************************************************************************** +* MegaTronics +* +****************************************************************************************/ +#if MOTHERBOARD == 70 +#define KNOWN_BOARD 1 + +//////////////////FIX THIS////////////// + + #ifndef __AVR_ATmega2560__ + #error Oops! Make sure you have 'Arduino Mega' selected from the 'Tools -> Boards' menu. + #endif + + + + +#define X_STEP_PIN 26 +#define X_DIR_PIN 28 +#define X_ENABLE_PIN 24 +#define X_MIN_PIN 41 +#define X_MAX_PIN 37 //2 //Max endstops default to disabled "-1", set to commented value to enable. + +#define Y_STEP_PIN 60 // A6 +#define Y_DIR_PIN 61 // A7 +#define Y_ENABLE_PIN 22 +#define Y_MIN_PIN 14 +#define Y_MAX_PIN 15 //15 + +#define Z_STEP_PIN 54 // A0 +#define Z_DIR_PIN 55 // A1 +#define Z_ENABLE_PIN 56 // A2 +#define Z_MIN_PIN 18 +#define Z_MAX_PIN 19 + +#define E0_STEP_PIN 31 +#define E0_DIR_PIN 32 +#define E0_ENABLE_PIN 38 + +#define E1_STEP_PIN 34 +#define E1_DIR_PIN 36 +#define E1_ENABLE_PIN 30 + +#define SDPOWER -1 +#define SDSS 53 +#define LED_PIN 13 + + +#define FAN_PIN 7 // IO pin. Buffer needed +#define PS_ON_PIN 12 +#define KILL_PIN -1 + +#define HEATER_0_PIN 9 // EXTRUDER 1 +#define HEATER_1_PIN 8 // EXTRUDER 2 (FAN On Sprinter) +#define HEATER_2_PIN -1 + +#if TEMP_SENSOR_0 == -1 +#define TEMP_0_PIN 8 // ANALOG NUMBERING +#else +#define TEMP_0_PIN 13 // ANALOG NUMBERING + +#endif + +#define TEMP_1_PIN 15 // ANALOG NUMBERING +#define TEMP_2_PIN -1 // ANALOG NUMBERING +#define HEATER_BED_PIN 10 // BED +#define TEMP_BED_PIN 14 // ANALOG NUMBERING + +#define BEEPER 33 // Beeper on AUX-4 + + +#ifdef ULTRA_LCD + + #ifdef NEWPANEL + //arduino pin which triggers an piezzo beeper + + #define LCD_PINS_RS 16 + #define LCD_PINS_ENABLE 17 + #define LCD_PINS_D4 23 + #define LCD_PINS_D5 25 + #define LCD_PINS_D6 27 + #define LCD_PINS_D7 29 + + //buttons are directly attached using AUX-2 + #define BTN_EN1 37 + #define BTN_EN2 35 + #define BTN_ENC 43 //the click + + #define BLEN_C 2 + #define BLEN_B 1 + #define BLEN_A 0 + + #define SDCARDDETECT -1 // Ramps does not use this port + + //encoder rotation values + #define encrot0 0 + #define encrot1 2 + #define encrot2 3 + #define encrot3 1 +#endif +#endif //ULTRA_LCD + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN, +#define E1_PINS + +#endif + + +#if MOTHERBOARD == 301 +#define KNOWN_BOARD +/***************************************************************** +* Rambo Pin Assignments +******************************************************************/ +#ifndef __AVR_ATmega2560__ +#error Oops! Make sure you have 'Arduino Mega 2560' selected from the 'Tools -> Boards' menu. +#endif + +#define X_STEP_PIN 37 +#define X_DIR_PIN 48 +#define X_MIN_PIN 12 +#define X_MAX_PIN 24 +#define X_ENABLE_PIN 29 +#define X_MS1_PIN 40 +#define X_MS2_PIN 41 + +#define Y_STEP_PIN 36 +#define Y_DIR_PIN 49 +#define Y_MIN_PIN 11 +#define Y_MAX_PIN 23 +#define Y_ENABLE_PIN 28 +#define Y_MS1_PIN 69 +#define Y_MS2_PIN 39 + +#define Z_STEP_PIN 35 +#define Z_DIR_PIN 47 +#define Z_MIN_PIN 10 +#define Z_MAX_PIN 30 +#define Z_ENABLE_PIN 27 +#define Z_MS1_PIN 68 +#define Z_MS2_PIN 67 + +#define HEATER_BED_PIN 3 +#define TEMP_BED_PIN 2 + +#define HEATER_0_PIN 9 +#define TEMP_0_PIN 0 + +#define HEATER_1_PIN -1 //7 +#define TEMP_1_PIN 1 + + +#define HEATER_2_PIN -1 +#define TEMP_2_PIN -1 + +#define E0_STEP_PIN 34 +#define E0_DIR_PIN 43 +#define E0_ENABLE_PIN 26 +#define E0_MS1_PIN 65 +#define E0_MS2_PIN 66 + +#define E1_STEP_PIN 33 +#define E1_DIR_PIN 42 +#define E1_ENABLE_PIN 25 +#define E1_MS1_PIN 63 +#define E1_MS2_PIN 64 + +#define DIGIPOTSS_PIN 38 +#define DIGIPOT_CHANNELS {4,5,3,0,1} // X Y Z E0 E1 digipot channels to stepper driver mapping +#define STEPPER_CURRENT_CONTROL CURRENT_CONTROL_DIGIPOT + + +#define SDPOWER -1 +#define SDSS 53 +#define LED_PIN 13 +#define FAN_PIN 8 +#define PS_ON_PIN 4 +#define KILL_PIN 80 +#define SUICIDE_PIN -1 //PIN that has to be turned on right after start, to keep power flowing. + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN,E0_MS1_PIN,E0_MS2_PIN, +#define E1_PINS + +#define SCK_PIN 52 +#define MISO_PIN 50 +#define MOSI_PIN 51 +#define MAX6675_SS 53 + +#endif + +#if MOTHERBOARD == 401 +#ifndef __SAM3X8E__ +#error Oops! Make sure you have 'Arduino Due' selected from the 'Tools -> Boards' menu. +#endif + +#define KNOWN_BOARD +#define CPU_ARCH ARCH_ARM +/***************************************************************** +* Arduino Due Pin Assignments +******************************************************************/ + +#define X_STEP_PIN 36 +#define X_DIR_PIN 48 +#define X_MIN_PIN 12 +#define X_MAX_PIN 19 +#define X_ENABLE_PIN 29 +#define X_MS1_PIN 40 +#define X_MS2_PIN 41 + +#define Y_STEP_PIN 36 +#define Y_DIR_PIN 49 +#define Y_MIN_PIN 11 +#define Y_MAX_PIN 18 +#define Y_ENABLE_PIN 28 +#define Y_MS1_PIN 69 +#define Y_MS2_PIN 39 + +#define Z_STEP_PIN 35 +#define Z_DIR_PIN 47 +#define Z_MIN_PIN 10 +#define Z_MAX_PIN 15 +#define Z_ENABLE_PIN 27 +#define Z_MS1_PIN 68 +#define Z_MS2_PIN 67 + +#define HEATER_BED_PIN 3 +#define TEMP_BED_PIN 65 + +#define HEATER_0_PIN 9 +#define TEMP_0_PIN 63 + +#define HEATER_1_PIN 7 +#define TEMP_1_PIN 64 + +#define HEATER_2_PIN -1 +#define TEMP_2_PIN -1 + +#define E0_STEP_PIN 34 +#define E0_DIR_PIN 43 +#define E0_ENABLE_PIN 26 +#define E0_MS1_PIN 65 +#define E0_MS2_PIN 66 + +#define E1_STEP_PIN 33 +#define E1_DIR_PIN 42 +#define E1_ENABLE_PIN 25 +#define E1_MS1_PIN 63 +#define E1_MS2_PIN 64 + +#define SDPOWER -1 +#define SDSS 53 +#define LED_PIN 13 +#define FAN_PIN 8 +#define PS_ON_PIN 4 +#define KILL_PIN -1 +#define SUICIDE_PIN -1 //PIN that has to be turned on right after start, to keep power flowing. + +#define E0_PINS E0_STEP_PIN,E0_DIR_PIN,E0_ENABLE_PIN,E0_MS1_PIN,E0_MS2_PIN, +#define E1_PINS + +#endif + +#ifndef CPU_ARCH // Set default architecture +#define CPU_ARCH ARCH_AVR +#endif + +#ifndef STEPPER_CURRENT_CONTROL // Set default stepper current control if not set yet. +#define STEPPER_CURRENT_CONTROL CURRENT_CONTROL_MANUAL +#endif + +#ifndef FAN_BOARD_PIN +#define FAN_BOARD_PIN -1 +#endif + +#if NUM_EXTRUDER==1 +#define E1_PINS +#endif + +#if NUM_EXTRUDER<3 +#define E2_PINS +#endif + +#define SENSITIVE_PINS {0, 1, X_STEP_PIN, X_DIR_PIN, X_ENABLE_PIN, X_MIN_PIN, X_MAX_PIN, Y_STEP_PIN, Y_DIR_PIN, Y_ENABLE_PIN, Y_MIN_PIN, Y_MAX_PIN, Z_STEP_PIN, Z_DIR_PIN, Z_ENABLE_PIN, Z_MIN_PIN, Z_MAX_PIN, LED_PIN, PS_ON_PIN, \ + HEATER_0_PIN, HEATER_1_PIN, FAN_PIN, E0_PINS E1_PINS E2_PINS TEMP_0_PIN, TEMP_1_PIN,SDSS } +#endif + diff --git a/Repetier/ui.cpp b/Repetier/ui.cpp new file mode 100644 index 0000000..d7eaa90 --- /dev/null +++ b/Repetier/ui.cpp @@ -0,0 +1,2291 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + +*/ + +#define UI_MAIN +#include "Reptier.h" +#include +extern const int8_t encoder_table[16] PROGMEM ; +#include "ui.h" +#include +#include +#include +#include +#include +#include +#include "Eeprom.h" +#include + +#if UI_ENCODER_SPEED==0 +const int8_t encoder_table[16] PROGMEM = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0}; // Full speed +#elif UI_ENCODER_SPEED==1 +const int8_t encoder_table[16] PROGMEM = {0,0,-1,0,0,0,0,1,1,0,0,0,0,-1,0,0}; // Half speed +#else +const int8_t encoder_table[16] PROGMEM = {0,0,0,0,0,0,0,0,1,0,0,0,0,-1,0,0}; // Quart speed +#endif + + +#if BEEPER_TYPE==2 && defined(UI_HAS_I2C_KEYS) && UI_I2C_KEY_ADDRESS!=BEEPER_ADDRESS +#error Beeper address and i2c key address must be identical +#else +#if BEEPER_TYPE==2 +#define UI_I2C_KEY_ADDRESS BEEPER_ADDRESS +#endif +#endif + +#if UI_AUTORETURN_TO_MENU_AFTER!=0 +long ui_autoreturn_time=0; +#endif + +void beep(byte duration,byte count) +{ +#if FEATURE_BEEPER +#if BEEPER_TYPE!=0 +#if BEEPER_TYPE==1 + SET_OUTPUT(BEEPER_PIN); +#endif +#if BEEPER_TYPE==2 + i2c_start_wait(BEEPER_ADDRESS+I2C_WRITE); +#if UI_DISPLAY_I2C_CHIPTYPE==1 + i2c_write( 0x14); // Start at port a +#endif +#endif + for(byte i=0;i>8); +#endif +#endif + delay(duration); +#if BEEPER_TYPE==1 + WRITE(BEEPER_PIN,LOW); +#else +#if UI_DISPLAY_I2C_CHIPTYPE==0 + +#if BEEPER_ADDRESS == UI_DISPLAY_I2C_ADDRESS + i2c_write((BEEPER_PIN) | uid.outputMask); +#else + i2c_write(255); +#endif +#endif +#if UI_DISPLAY_I2C_CHIPTYPE==1 + i2c_write( uid.outputMask); + i2c_write(uid.outputMask>>8); +#endif +#endif + delay(duration); + } +#if BEEPER_TYPE==2 + i2c_stop(); +#endif +#endif +#endif +} + +//============================================================= +// I2C driver +//============================================================= + +#ifdef COMPILE_I2C_DRIVER +/************************************************************************* +* Title: I2C master library using hardware TWI interface +* Author: Peter Fleury http://jump.to/fleury +* File: $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $ +* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3 +* Target: any AVR device with hardware TWI +* Usage: API compatible with I2C Software Library i2cmaster.h +**************************************************************************/ +#include +#include + +/* I2C clock in Hz */ +#define SCL_CLOCK UI_I2C_CLOCKSPEED + + +/************************************************************************* + Initialization of the I2C bus interface. Need to be called only once +*************************************************************************/ +inline void i2c_init(void) +{ + /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */ + uid.outputMask = UI_DISPLAY_I2C_OUTPUT_START_MASK; + TWSR = 0; /* no prescaler */ + TWBR = ((F_CPU/SCL_CLOCK)-16)/2; /* must be > 10 for stable operation */ +#if UI_DISPLAY_I2C_CHIPTYPE==0 && BEEPER_TYPE==2 && BEEPER_PIN>=0 +#if BEEPER_ADDRESS == UI_DISPLAY_I2C_ADDRESS + uid.outputMask |= BEEPER_PIN +#endif +#endif +#if UI_DISPLAY_I2C_CHIPTYPE==1 + // set direction of pins + i2c_start(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); + i2c_write(0); // IODIRA + i2c_write(~(UI_DISPLAY_I2C_OUTPUT_PINS & 255)); +// i2c_stop(); +// i2c_start(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); +// i2c_write(1); // IODIRB + i2c_write(~(UI_DISPLAY_I2C_OUTPUT_PINS >> 8)); + i2c_stop(); + // Set pullups according to UI_DISPLAY_I2C_PULLUP + i2c_start(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); + i2c_write(0x0C); // GPPUA + i2c_write(UI_DISPLAY_I2C_PULLUP & 255); +// i2c_stop(); +// i2c_start(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); +// i2c_write(0x0D); // GPPUB + i2c_write(UI_DISPLAY_I2C_PULLUP >> 8); + i2c_stop(); +#endif +}/* i2c_init */ + + +/************************************************************************* + Issues a start condition and sends address and transfer direction. + return 0 = device accessible, 1= failed to access device +*************************************************************************/ +unsigned char i2c_start(unsigned char address) +{ + uint8_t twst; + + // send START condition + TWCR = (1<> 8); + i2c_write(v & 255);i2c_write(v >> 8); +#endif +} +void lcdWriteByte(byte c,byte rs) { +#if UI_DISPLAY_I2C_CHIPTYPE==0 + byte mod = (rs?UI_DISPLAY_RS_PIN:0) | uid.outputMask; // | (UI_DISPLAY_RW_PIN); +#if UI_DISPLAY_D4_PIN==1 && UI_DISPLAY_D5_PIN==2 && UI_DISPLAY_D6_PIN==4 && UI_DISPLAY_D7_PIN==8 + byte value = (c >> 4) | mod; + i2c_write((value) | UI_DISPLAY_ENABLE_PIN); + i2c_write(value); + value = (c & 15) | mod; + i2c_write((value) | UI_DISPLAY_ENABLE_PIN); + i2c_write(value); +#else + byte value = (c & 16?UI_DISPLAY_D4_PIN:0)|(c & 32?UI_DISPLAY_D5_PIN:0)|(c & 64?UI_DISPLAY_D6_PIN:0)|(c & 128?UI_DISPLAY_D7_PIN:0) | mod; + i2c_write((value) | UI_DISPLAY_ENABLE_PIN); + i2c_write(value); + value = (c & 1?UI_DISPLAY_D4_PIN:0)|(c & 2?UI_DISPLAY_D5_PIN:0)|(c & 4?UI_DISPLAY_D6_PIN:0)|(c & 8?UI_DISPLAY_D7_PIN:0) | mod; + i2c_write((value) | UI_DISPLAY_ENABLE_PIN); + i2c_write(value); +#endif +#endif +#if UI_DISPLAY_I2C_CHIPTYPE==1 + unsigned int mod = (rs?UI_DISPLAY_RS_PIN:0) | uid.outputMask; // | (UI_DISPLAY_RW_PIN); + unsigned int value = (c & 16?UI_DISPLAY_D4_PIN:0)|(c & 32?UI_DISPLAY_D5_PIN:0)|(c & 64?UI_DISPLAY_D6_PIN:0)|(c & 128?UI_DISPLAY_D7_PIN:0) | mod; + unsigned int value2 = (value) | UI_DISPLAY_ENABLE_PIN; + i2c_write(value2 & 255);i2c_write(value2 >>8); + i2c_write(value & 255);i2c_write(value>>8); + value = (c & 1?UI_DISPLAY_D4_PIN:0)|(c & 2?UI_DISPLAY_D5_PIN:0)|(c & 4?UI_DISPLAY_D6_PIN:0)|(c & 8?UI_DISPLAY_D7_PIN:0) | mod; + value2 = (value) | UI_DISPLAY_ENABLE_PIN; + i2c_write(value2 & 255);i2c_write(value2 >>8); + i2c_write(value & 255);i2c_write(value>>8); +#endif +} +void initializeLCD() { + delay(135); + lcdStartWrite(); + i2c_write(uid.outputMask & 255); +#if UI_DISPLAY_I2C_CHIPTYPE==1 + i2c_write(uid.outputMask >> 16); +#endif + delayMicroseconds(10); + lcdWriteNibble(0x03); + delayMicroseconds(5000); // I have one LCD for which 4500 here was not long enough. + // second try + lcdWriteNibble(0x03); + delayMicroseconds(150); // wait + // third go! + lcdWriteNibble(0x03); + delayMicroseconds(150); + // finally, set to 4-bit interface + lcdWriteNibble(0x02); + delayMicroseconds(150); + // finally, set # lines, font size, etc. + lcdCommand(LCD_4BIT | LCD_2LINE | LCD_5X7); + lcdCommand(LCD_CLEAR); //- Clear Screen + delay(2); // clear is slow operation + lcdCommand(LCD_INCREASE | LCD_DISPLAYSHIFTOFF); //- Entrymode (Display Shift: off, Increment Address Counter) + lcdCommand(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKINGOFF); //- Display on + uid.lastSwitch = uid.lastRefresh = millis(); + uid.createChar(1,character_back); + uid.createChar(2,character_degree); + uid.createChar(3,character_selected); + uid.createChar(4,character_unselected); + uid.createChar(5,character_temperature); + uid.createChar(6,character_folder); + lcdStopWrite(); +} +#endif +#if UI_DISPLAY_TYPE==1 || UI_DISPLAY_TYPE==2 + +void lcdWriteNibble(byte value) { + WRITE(UI_DISPLAY_D4_PIN,value & 1); + WRITE(UI_DISPLAY_D5_PIN,value & 2); + WRITE(UI_DISPLAY_D6_PIN,value & 4); + WRITE(UI_DISPLAY_D7_PIN,value & 8); + WRITE(UI_DISPLAY_ENABLE_PIN, HIGH);// enable pulse must be >450ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + WRITE(UI_DISPLAY_ENABLE_PIN, LOW); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} +void lcdWriteByte(byte c,byte rs) { +#if UI_DISPLAY_RW_PIN<0 + delayMicroseconds(UI_DELAYPERCHAR); +#else + SET_INPUT(UI_DISPLAY_D4_PIN); + SET_INPUT(UI_DISPLAY_D5_PIN); + SET_INPUT(UI_DISPLAY_D6_PIN); + SET_INPUT(UI_DISPLAY_D7_PIN); + WRITE(UI_DISPLAY_RW_PIN, HIGH); + WRITE(UI_DISPLAY_RS_PIN, LOW); + uint8_t busy; + do { + WRITE(UI_DISPLAY_ENABLE_PIN, HIGH); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + busy = READ(UI_DISPLAY_D7_PIN); + WRITE(UI_DISPLAY_ENABLE_PIN, LOW); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + WRITE(UI_DISPLAY_ENABLE_PIN, HIGH); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + WRITE(UI_DISPLAY_ENABLE_PIN, LOW); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + } while (busy); + SET_OUTPUT(UI_DISPLAY_D4_PIN); + SET_OUTPUT(UI_DISPLAY_D5_PIN); + SET_OUTPUT(UI_DISPLAY_D6_PIN); + SET_OUTPUT(UI_DISPLAY_D7_PIN); + WRITE(UI_DISPLAY_RW_PIN, LOW); +#endif + WRITE(UI_DISPLAY_RS_PIN, rs); + + WRITE(UI_DISPLAY_D4_PIN, c & 0x10); + WRITE(UI_DISPLAY_D5_PIN, c & 0x20); + WRITE(UI_DISPLAY_D6_PIN, c & 0x40); + WRITE(UI_DISPLAY_D7_PIN, c & 0x80); + WRITE(UI_DISPLAY_ENABLE_PIN, HIGH); // enable pulse must be >450ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + WRITE(UI_DISPLAY_ENABLE_PIN, LOW); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + WRITE(UI_DISPLAY_D4_PIN, c & 0x01); + WRITE(UI_DISPLAY_D5_PIN, c & 0x02); + WRITE(UI_DISPLAY_D6_PIN, c & 0x04); + WRITE(UI_DISPLAY_D7_PIN, c & 0x08); + WRITE(UI_DISPLAY_ENABLE_PIN, HIGH); // enable pulse must be >450ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + WRITE(UI_DISPLAY_ENABLE_PIN, LOW); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} +void initializeLCD() { + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way before 4.5V. + // is this delay long enough for all cases?? + delay(135); + SET_OUTPUT(UI_DISPLAY_D4_PIN); + SET_OUTPUT(UI_DISPLAY_D5_PIN); + SET_OUTPUT(UI_DISPLAY_D6_PIN); + SET_OUTPUT(UI_DISPLAY_D7_PIN); + SET_OUTPUT(UI_DISPLAY_RS_PIN); +#if UI_DISPLAY_RW_PIN>-1 + SET_OUTPUT(UI_DISPLAY_RW_PIN); +#endif + SET_OUTPUT(UI_DISPLAY_ENABLE_PIN); + + // Now we pull both RS and R/W low to begin commands + WRITE(UI_DISPLAY_RS_PIN, LOW); + WRITE(UI_DISPLAY_ENABLE_PIN, LOW); + + //put the LCD into 4 bit mode + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + // at this point we are in 8 bit mode but of course in this + // interface 4 pins are dangling unconnected and the values + // on them don't matter for these instructions. + WRITE(UI_DISPLAY_RS_PIN, LOW); + delayMicroseconds(10); + lcdWriteNibble(0x03); + delayMicroseconds(5000); // I have one LCD for which 4500 here was not long enough. + // second try + lcdWriteNibble(0x03); + delayMicroseconds(150); // wait + // third go! + lcdWriteNibble(0x03); + delayMicroseconds(150); + // finally, set to 4-bit interface + lcdWriteNibble(0x02); + delayMicroseconds(150); + // finally, set # lines, font size, etc. + lcdCommand(LCD_4BIT | LCD_2LINE | LCD_5X7); + + lcdCommand(LCD_CLEAR); //- Clear Screen + delay(2); // clear is slow operation + lcdCommand(LCD_INCREASE | LCD_DISPLAYSHIFTOFF); //- Entrymode (Display Shift: off, Increment Address Counter) + lcdCommand(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKINGOFF); //- Display on + uid.lastSwitch = uid.lastRefresh = millis(); + uid.createChar(1,character_back); + uid.createChar(2,character_degree); + uid.createChar(3,character_selected); + uid.createChar(4,character_unselected); + uid.createChar(5,character_temperature); + uid.createChar(6,character_folder); +} +// ----------- end direct LCD driver +#endif + +#if UI_DISPLAY_TYPE==4 +// Use LiquidCrystal library instead +#include + +LiquidCrystal lcd(UI_DISPLAY_RS_PIN, UI_DISPLAY_RW_PIN,UI_DISPLAY_ENABLE_PIN,UI_DISPLAY_D4_PIN,UI_DISPLAY_D5_PIN,UI_DISPLAY_D6_PIN,UI_DISPLAY_D7_PIN); + +void UIDisplay::createChar(byte location,const byte charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + byte data[8]; + for (int i=0; i<8; i++) { + data[i]=pgm_read_byte(&(charmap[i])); + } + lcd.createChar(location, data); +} +void UIDisplay::printRow(byte r,char *txt) { + byte col=0; + // Set row + if(r >= UI_ROWS) return; + lcd.setCursor(0,r); + char c; + while(col0 + initializeLCD(); + uid.printRowP(0,versionString); + uid.printRowP(1,versionString2); +#endif +#if BEEPER_TYPE==2 || defined(UI_HAS_I2C_KEYS) + // Make sure the beeper is off + i2c_start_wait(UI_I2C_KEY_ADDRESS+I2C_WRITE); + i2c_write(255); // Disable beeper, enable read for other pins. + i2c_stop(); +#endif +} +#if UI_DISPLAY_TYPE==1 || UI_DISPLAY_TYPE==2 || UI_DISPLAY_TYPE==3 +void UIDisplay::createChar(byte location,const byte PROGMEM charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + lcdCommand(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + lcdPutChar(pgm_read_byte(&(charmap[i]))); + } +} +void UIDisplay::printRow(byte r,char *txt) { + byte col=0; + // Set row + if(r >= UI_ROWS) return; +#if UI_DISPLAY_TYPE==3 + lcdStartWrite(); +#endif + lcdWriteByte(128 + pgm_read_byte(&LCDLineOffsets[r]),0); // Position cursor + char c; + while(col0 + ui_check_slow_encoder(); +#endif +} +#endif + +void UIDisplay::printRowP(byte r,PGM_P txt) { + if(r >= UI_ROWS) return; + col=0; + addStringP(txt); + printCols[col]=0; + printRow(r,printCols); +} +void UIDisplay::addInt(int value,byte digits) { + byte dig=0,neg=0; + if(value<0) { + value = -value; + neg=1; + dig++; + } + char buf[7]; // Assumes 8-bit chars plus zero byte. + char *str = &buf[6]; + buf[6]=0; + do { + unsigned int m = value; + value /= 10; + char c = m - 10 * value; + *--str = c + '0'; + dig++; + } while(value); + if(neg) + printCols[col++]='-'; + if(digits<6) + while(dig=UI_COLS) return; + number = -number; + fixdigits--; + } + number += pgm_read_float(&roundingTable[digits]); // for correct rounding + + // Extract the integer part of the number and print it + unsigned long int_part = (unsigned long)number; + float remainder = number - (float)int_part; + addLong(int_part,fixdigits); + if(col>=UI_COLS) return; + + // Print the decimal point, but only if there are digits beyond + if (digits > 0) { + printCols[col++]='.'; + } + + // Extract digits from the remainder one at a time + while (col 0) + { + remainder *= 10.0; + byte toPrint = byte(remainder); + printCols[col++] = '0'+toPrint; + remainder -= toPrint; + } +} +void UIDisplay::addStringP(PGM_P text) { + while(coltempControl.currentTemperatureC; + else if(c2>='0' && c2<='9') fvalue=extruder[c2-'0'].tempControl.currentTemperatureC; + else if(c2=='b') fvalue=heated_bed_get_temperature(); + else if(c2=='B') {ivalue=0;fvalue=heated_bed_get_temperature();} + addFloat(fvalue,3,ivalue); + break; + case 'E': // Target extruder temperature + if(c2=='c') fvalue=current_extruder->tempControl.targetTemperatureC; + else if(c2>='0' && c2<='9') fvalue=extruder[c2-'0'].tempControl.targetTemperatureC; +#if HAVE_HEATED_BED + else if(c2=='b') fvalue=heatedBedController.targetTemperatureC; +#endif + addFloat(fvalue,3,0 /*UI_TEMP_PRECISION*/); + break; + + case 'f': + if(c2=='x') addFloat(max_feedrate[0],5,0); + else if(c2=='y') addFloat(max_feedrate[1],5,0); + else if(c2=='z') addFloat(max_feedrate[2],5,0); + else if(c2=='X') addFloat(homing_feedrate[0],5,0); + else if(c2=='Y') addFloat(homing_feedrate[1],5,0); + else if(c2=='Z') addFloat(homing_feedrate[2],5,0); + break; + case 'i': + if(c2=='s') addLong(stepper_inactive_time,4); + else if(c2=='p') addLong(max_inactive_time,4); + break; + case 'O': // ops related stuff + #if USE_OPS==1 + if(c2=='0') addStringP(printer_state.opsMode==0?ui_selected:ui_unselected); + else if(c2=='1') addStringP(printer_state.opsMode==1?ui_selected:ui_unselected); + else if(c2=='2') addStringP(printer_state.opsMode==2?ui_selected:ui_unselected); + else if(c2=='r') addFloat(printer_state.opsRetractDistance,2,1); + else if(c2=='b') addFloat(printer_state.opsRetractBacklash,2,1); + else if(c2=='d') addFloat(printer_state.opsMinDistance,2,1); + else if(c2=='a') { + addFloat(printer_state.opsMoveAfter,3,0); + if(col>8)*100/(sd.filesize>>8); + addInt((int)percent,3); + if(col='0' && c2<='9') ivalue=pwm_pos[c2-'0']; +#if HAVE_HEATED_BED + else if(c2=='b') ivalue=pwm_pos[heatedBedController.pwmIndex]; +#endif + else if(c2=='C') ivalue=pwm_pos[current_extruder->id]; + ivalue=(ivalue*100)/255; + addInt(ivalue,3); + if(col='0' && c2<='3') +#if NUM_EXTRUDER>0 + if(c2=='0') + fvalue = (float)(printer_state.currentPositionSteps[c2-'0']+current_extruder->xOffset)*inv_axis_steps_per_unit[c2-'0']; + else if(c2=='1') + fvalue = (float)(printer_state.currentPositionSteps[c2-'0']+current_extruder->yOffset)*inv_axis_steps_per_unit[c2-'0']; + else + fvalue = (float)printer_state.currentPositionSteps[c2-'0']*inv_axis_steps_per_unit[c2-'0']; +#else + fvalue = (float)printer_state.currentPositionSteps[c2-'0']*inv_axis_steps_per_unit[c2-'0']; +#endif + addFloat(fvalue,3,2); + break; + case 'y': +#if DRIVE_SYSTEM==3 + if(c2>='0' && c2<='3') fvalue = (float)printer_state.currentDeltaPositionSteps[c2-'0']*inv_axis_steps_per_unit[c2-'0']; + addFloat(fvalue,3,2); +#endif + break; + case 'X': // Extruder related +#if NUM_EXTRUDER>0 + if(c2>='0' && c2<='9') {addStringP(current_extruder->id==c2-'0'?ui_selected:ui_unselected);} +#ifdef TEMP_PID + else if(c2=='i') {addFloat(current_extruder->tempControl.pidIGain,4,2);} + else if(c2=='p') {addFloat(current_extruder->tempControl.pidPGain,4,2);} + else if(c2=='d') {addFloat(current_extruder->tempControl.pidDGain,4,2);} + else if(c2=='m') {addInt(current_extruder->tempControl.pidDriveMin,3);} + else if(c2=='M') {addInt(current_extruder->tempControl.pidDriveMax,3);} + else if(c2=='D') {addInt(current_extruder->tempControl.pidMax,3);} +#endif + else if(c2=='w') {addInt(current_extruder->watchPeriod,4);} +#if RETRACT_DURING_HEATUP + else if(c2=='T') {addInt(current_extruder->waitRetractTemperature,4);} + else if(c2=='U') {addInt(current_extruder->waitRetractUnits,2);} +#endif + else if(c2=='h') {addStringP(!current_extruder->tempControl.heatManager?PSTR(UI_TEXT_STRING_HM_BANGBANG):PSTR(UI_TEXT_STRING_HM_PID));} +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + else if(c2=='a') {addFloat(current_extruder->advanceK,3,0);} +#endif + else if(c2=='l') {addFloat(current_extruder->advanceL,3,0);} +#endif + else if(c2=='x') {addFloat(current_extruder->xOffset,4,2);} + else if(c2=='y') {addFloat(current_extruder->yOffset,4,2);} + else if(c2=='f') {addFloat(current_extruder->maxStartFeedrate,5,0);} + else if(c2=='F') {addFloat(current_extruder->maxFeedrate,5,0);} + else if(c2=='A') {addFloat(current_extruder->maxAcceleration,5,0);} +#endif + break; + case 's': // Endstop positions + if(c2=='x') { + #if (X_MIN_PIN > -1) && MIN_HARDWARE_ENDSTOP_X + addStringP((READ(X_MIN_PIN)^ENDSTOP_X_MIN_INVERTING)?ui_text_on:ui_text_off); + #else + addStringP(ui_text_na); + #endif + } + if(c2=='X') + #if (X_MAX_PIN > -1) && MAX_HARDWARE_ENDSTOP_X + addStringP((READ(X_MAX_PIN)^ENDSTOP_X_MAX_INVERTING)?ui_text_on:ui_text_off); + #else + addStringP(ui_text_na); + #endif + if(c2=='y') + #if (Y_MIN_PIN > -1)&& MIN_HARDWARE_ENDSTOP_Y + addStringP((READ(Y_MIN_PIN)^ENDSTOP_Y_MIN_INVERTING)?ui_text_on:ui_text_off); + #else + addStringP(ui_text_na); + #endif + if(c2=='Y') + #if (Y_MAX_PIN > -1) && MAX_HARDWARE_ENDSTOP_Y + addStringP((READ(Y_MAX_PIN)^ENDSTOP_Y_MAX_INVERTING)?ui_text_on:ui_text_off); + #else + addStringP(ui_text_na); + #endif + if(c2=='z') + #if (Z_MIN_PIN > -1) && MIN_HARDWARE_ENDSTOP_Z + addStringP((READ(Z_MIN_PIN)^ENDSTOP_Z_MIN_INVERTING)?ui_text_on:ui_text_off); + #else + addStringP(ui_text_na); + #endif + if(c2=='Z') + #if (Z_MAX_PIN > -1) && MAX_HARDWARE_ENDSTOP_Z + addStringP((READ(Z_MAX_PIN)^ENDSTOP_Z_MAX_INVERTING)?ui_text_on:ui_text_off); + #else + addStringP(ui_text_na); + #endif + break; + case 'S': + if(c2=='x') addFloat(axis_steps_per_unit[0],3,1); + if(c2=='y') addFloat(axis_steps_per_unit[1],3,1); + if(c2=='z') addFloat(axis_steps_per_unit[2],3,1); + if(c2=='e') addFloat(current_extruder->stepsPerMM,3,1); + break; + } + } + printCols[col] = 0; +} +void UIDisplay::setStatusP(PGM_P txt) { + byte i=0; + while(i<16) { + byte c = pgm_read_byte(txt++); + if(!c) break; + statusMsg[i++] = c; + } + statusMsg[i]=0; +} +void UIDisplay::setStatus(char *txt) { + byte i=0; + while(*txt && i<16) + statusMsg[i++] = *txt++; + statusMsg[i]=0; +} + +const UIMenu * const ui_pages[UI_NUM_PAGES] PROGMEM = UI_PAGES; +#if SDSUPPORT +byte nFilesOnCard; + +void UIDisplay::updateSDFileCount() { + dir_t* p; + byte offset = menuTop[menuLevel]; + SdBaseFile *root = sd.fat.vwd(); + root->rewind(); + nFilesOnCard = 0; + while ((p = root->readDirCache())) { + // done if past last used entry + if (p->name[0] == DIR_NAME_FREE) break; + // skip deleted entry and entries for . and .. + if(!sd.showFilename(p->name) && !(p->name[0]=='.' && p->name[1]=='.')) continue; + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; + if(folderLevel>=SD_MAX_FOLDER_DEPTH && DIR_IS_SUBDIR(p) && !(p->name[0]=='.' && p->name[1]=='.')) continue; + nFilesOnCard++; + if(nFilesOnCard==254) return; + } +} +void getSDFilenameAt(byte filePos,char *filename) { + dir_t* p; + byte c=0; + SdBaseFile *root = sd.fat.vwd(); + root->rewind(); + while ((p = root->readDirCache())) { + // done if past last used entry + if (p->name[0] == DIR_NAME_FREE) break; + // skip deleted entry and entries for . and .. + if(!sd.showFilename(p->name) && !(p->name[0]=='.' && p->name[1]=='.')) continue; + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; + if(uid.folderLevel>=SD_MAX_FOLDER_DEPTH && DIR_IS_SUBDIR(p) && !(p->name[0]=='.' && p->name[1]=='.')) continue; + if(filePos) { + filePos--; + continue; + } + for (uint8_t i = 0; i < 11; i++) { + if (p->name[i] == ' ')continue; + if (i == 8) + filename[c++]='.'; + filename[c++]=tolower(p->name[i]); + } + if(DIR_IS_SUBDIR(p)) filename[c++]='/'; // Set marker for directory + break; + } + filename[c]=0; +} +bool UIDisplay::isDirname(char *name) { + while(*name) name++; + name--; + return *name=='/'; +} +void UIDisplay::goDir(char *name) { + char *p = cwd; + while(*p)p++; + if(name[0]=='.' && name[1]=='.') { + if(folderLevel==0) return; + p--;p--; + while(*p!='/') p--; + p++; + *p = 0; + folderLevel--; + } else { + if(folderLevel>=SD_MAX_FOLDER_DEPTH) return; + while(*name) *p++ = *name++; + *p = 0; + folderLevel++; + } + sd.fat.chdir(cwd); + updateSDFileCount(); +} +void UIDisplay::sdrefresh(byte &r) { + dir_t* p; + byte offset = menuTop[menuLevel]; + sd.fat.chdir(cwd); + SdBaseFile *root = sd.fat.vwd(); + root->rewind(); + byte skip = (offset>0?offset-1:0); + while (r+offsetreadDirCache())) { + // done if past last used entry + if (p->name[0] == DIR_NAME_FREE) break; + // skip deleted entry and entries for . and .. + if(!sd.showFilename(p->name) && !(p->name[0]=='.' && p->name[1]=='.')) continue; + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; + if(folderLevel>=SD_MAX_FOLDER_DEPTH && DIR_IS_SUBDIR(p) && !(p->name[0]=='.' && p->name[1]=='.')) continue; + if(skip>0) {skip--;continue;} + col=0; + if(r+offset==menuPos[menuLevel]) + printCols[col++]='>'; + else + printCols[col++]=' '; + // print file name with possible blank fill + if(DIR_IS_SUBDIR(p)) + printCols[col++] = 6; // Prepend folder symbol + else + printCols[col++] = ' '; + for (byte i = 0; i < 11; i++) { + if (p->name[i] == ' ')continue; + if (i == 8) + printCols[col++]='.'; + printCols[col++]=tolower(p->name[i]); + } + printCols[col]=0; + printRow(r,printCols); + r++; + } +} +#endif +// Refresh current menu page +void UIDisplay::refreshPage() { + byte r; + byte mtype; + if(menuLevel==0) { + UIMenu *men = (UIMenu*)pgm_read_word(&(ui_pages[menuPos[0]])); + byte nr = pgm_read_word_near(&(men->numEntries)); + UIMenuEntry **entries = (UIMenuEntry**)pgm_read_word(&(men->entries)); + for(r=0;rtext)),false); + printRow(r,(char*)printCols); + } + } else { + UIMenu *men = (UIMenu*)menu[menuLevel]; + byte nr = pgm_read_word_near((void*)&(men->numEntries)); + mtype = pgm_read_byte((void*)&(men->menuType)); + byte offset = menuTop[menuLevel]; + UIMenuEntry **entries = (UIMenuEntry**)pgm_read_word(&(men->entries)); + for(r=0;r+offsetmenuType)); + unsigned int entAction = pgm_read_word(&(ent->action)); + col=0; + if(entType>=2 && entType<=4) { + if(r+offset==menuPos[menuLevel] && activeAction!=entAction) + printCols[col++]=CHAR_SELECTOR; + else if(activeAction==entAction) + printCols[col++]=CHAR_SELECTED; + else + printCols[col++]=' '; + } + parse((char*)pgm_read_word(&(ent->text)),false); + if(entType==2) { // Draw submenu marker at the right side + while(colmenuType))==1) // Open files list + updateSDFileCount(); +#endif + if(refresh) + refreshPage(); +} +void UIDisplay::okAction() { +#if UI_HAS_KEYS==1 + if(menuLevel==0) { // Enter menu + menuLevel = 1; + menuTop[1] = menuPos[1] = 0; + menu[1] = (void*)&ui_menu_main; + BEEP_SHORT + return; + } + UIMenu *men = (UIMenu*)menu[menuLevel]; + //byte nr = pgm_read_word_near(&(menu->numEntries)); + byte mtype = pgm_read_byte(&(men->menuType)); + UIMenuEntry **entries = (UIMenuEntry**)pgm_read_word(&(men->entries)); + UIMenuEntry *ent =(UIMenuEntry *)pgm_read_word(&(entries[menuPos[menuLevel]])); + unsigned char entType = pgm_read_byte(&(ent->menuType));// 0 = Info, 1 = Headline, 2 = submenu ref, 3 = direct action command, 4 = modify action + int action = pgm_read_word(&(ent->action)); + if(mtype==3) { // action menu + action = pgm_read_word(&(men->id)); + finishAction(action); + executeAction(UI_ACTION_BACK); + return; + } + if(mtype==2 && entType==4) { // Modify action + if(activeAction) { // finish action + finishAction(action); + activeAction = 0; + } else + activeAction = action; + return; + } +#if SDSUPPORT + if(mtype==1) { + if(menuPos[menuLevel]==0) { // Selected back instead of file + executeAction(UI_ACTION_BACK); + return; + } + byte filePos = menuPos[menuLevel]-1; + char filename[14]; + getSDFilenameAt(filePos,filename); + if(isDirname(filename)) { // Directory change selected + goDir(filename); + menuTop[menuLevel]=0; + menuPos[menuLevel]=1; + refreshPage(); + return; + } + menuLevel--; + men = (UIMenu*)menu[menuLevel]; + entries = (UIMenuEntry**)pgm_read_word(&(men->entries)); + ent =(UIMenuEntry *)pgm_read_word(&(entries[menuPos[menuLevel]])); + switch(pgm_read_word(&(ent->action))) { + case UI_ACTION_SD_PRINT: + if(sd.sdactive){ + sd.sdmode = false; + sd.file.close(); + if (sd.file.open(filename, O_READ)) { + OUT_P("File opened:"); + out.print(filename); + OUT_P(" Size:"); + out.println(sd.file.fileSize()); + sd.sdpos = 0; + sd.filesize = sd.file.fileSize(); + OUT_P_LN("File selected"); + sd.sdmode = true; // Start print immediately + menuLevel = 0; + BEEP_LONG; + } + else{ + OUT_P_LN("file.open failed"); + } + } + break; + case UI_ACTION_SD_DELETE: + if(sd.sdactive){ + sd.sdmode = false; + sd.file.close(); + if(sd.fat.remove(filename)) { + OUT_P_LN("File deleted"); + BEEP_LONG + } else { + OUT_P_LN("Deletion failed"); + } + } + break; + } + } +#endif + if(entType==2) { // Enter submenu + pushMenu((void*)action,false); + BEEP_SHORT + return; + } + if(entType==3) { + executeAction(action); + return; + } + executeAction(UI_ACTION_BACK); +#endif +} +#define INCREMENT_MIN_MAX(a,steps,_min,_max) a+=increment*steps;if(a<(_min)) a=_min;else if(a>(_max)) a=_max; +void UIDisplay::nextPreviousAction(char next) { +#if UI_HAS_KEYS==1 + if(menuLevel==0) { + lastSwitch = millis(); + if((UI_INVERT_MENU_DIRECTION && next<0) || (!UI_INVERT_MENU_DIRECTION && next>0)) { + menuPos[0]++; + if(menuPos[0]>=UI_NUM_PAGES) + menuPos[0]=0; + } else { + if(menuPos[0]==0) + menuPos[0]=UI_NUM_PAGES-1; + else + menuPos[0]--; + } + return; + } + UIMenu *men = (UIMenu*)menu[menuLevel]; + byte nr = pgm_read_word_near(&(men->numEntries)); + byte mtype = pgm_read_byte(&(men->menuType)); + UIMenuEntry **entries = (UIMenuEntry**)pgm_read_word(&(men->entries)); + UIMenuEntry *ent =(UIMenuEntry *)pgm_read_word(&(entries[menuPos[menuLevel]])); + unsigned char entType = pgm_read_byte(&(ent->menuType));// 0 = Info, 1 = Headline, 2 = submenu ref, 3 = direct action command + int action = pgm_read_word(&(ent->action)); + if(mtype==2 && activeAction==0) { // browse through menu items + if((UI_INVERT_MENU_DIRECTION && next<0) || (!UI_INVERT_MENU_DIRECTION && next>0)) { + if(menuPos[menuLevel]+10) + menuPos[menuLevel]--; + if(menuTop[menuLevel]>menuPos[menuLevel]) + menuTop[menuLevel]=menuPos[menuLevel]; + else if(menuTop[menuLevel]+UI_ROWS-10)) { + if(menuPos[menuLevel]0) + menuPos[menuLevel]--; + if(menuTop[menuLevel]>menuPos[menuLevel]) + menuTop[menuLevel]=menuPos[menuLevel]; + else if(menuTop[menuLevel]+UI_ROWS-1id)); else action=activeAction; + char increment = next; + switch(action) { + case UI_ACTION_XPOSITION: + move_steps(increment,0,0,0,homing_feedrate[0],true,true); + printPosition(); + break; + case UI_ACTION_YPOSITION: + move_steps(0,increment,0,0,homing_feedrate[1],true,true); + printPosition(); + break; + case UI_ACTION_ZPOSITION: + move_steps(0,0,increment,0,homing_feedrate[2],true,true); + printPosition(); + break; + case UI_ACTION_XPOSITION_FAST: + move_steps(axis_steps_per_unit[0]*increment,0,0,0,homing_feedrate[0],true,true); + printPosition(); + break; + case UI_ACTION_YPOSITION_FAST: + move_steps(0,axis_steps_per_unit[1]*increment,0,0,homing_feedrate[1],true,true); + printPosition(); + break; + case UI_ACTION_ZPOSITION_FAST: + move_steps(0,0,axis_steps_per_unit[2]*increment,0,homing_feedrate[2],true,true); + printPosition(); + break; + case UI_ACTION_EPOSITION: + move_steps(0,0,0,axis_steps_per_unit[3]*increment,UI_SET_EXTRUDER_FEEDRATE,true,false); + printPosition(); + break; + case UI_ACTION_HEATED_BED_TEMP: +#if HAVE_HEATED_BED==true + { + int tmp = (int)heatedBedController.targetTemperatureC; + if(tmpUI_SET_MAX_HEATED_BED_TEMP) tmp = UI_SET_MAX_HEATED_BED_TEMP; + heated_bed_set_temperature(tmp); + } +#endif + break; + case UI_ACTION_EXTRUDER0_TEMP: + { + int tmp = (int)extruder[0].tempControl.targetTemperatureC; + if(tmpUI_SET_MAX_EXTRUDER_TEMP) tmp = UI_SET_MAX_EXTRUDER_TEMP; + extruder_set_temperature(tmp,0); + } + break; + case UI_ACTION_EXTRUDER1_TEMP: + #if NUM_EXTRUDER>1 + { + int tmp = (int)extruder[1].tempControl.targetTemperatureC; + tmp+=increment; + if(tmp==1) tmp = UI_SET_MIN_EXTRUDER_TEMP; + if(tmpUI_SET_MAX_EXTRUDER_TEMP) tmp = UI_SET_MAX_EXTRUDER_TEMP; + extruder_set_temperature(tmp,1); + } + #endif + break; +#if USE_OPS==1 + case UI_ACTION_OPS_RETRACTDISTANCE: + printer_state.opsRetractDistance+=increment*0.1; + if(printer_state.opsRetractDistance<0) printer_state.opsRetractDistance=0; + else if(printer_state.opsRetractDistance>10) printer_state.opsRetractDistance=10; + extruder_select(current_extruder->id); + break; + case UI_ACTION_OPS_BACKLASH: + printer_state.opsRetractBacklash+=increment*0.1; + if(printer_state.opsRetractBacklash<-5) printer_state.opsRetractBacklash=-5; + else if(printer_state.opsRetractBacklash>5) printer_state.opsRetractBacklash=5; + extruder_select(current_extruder->id); + break; + case UI_ACTION_OPS_MOVE_AFTER: + printer_state.opsMoveAfter+=increment; + if(printer_state.opsMoveAfter<0) printer_state.opsMoveAfter=0; + else if(printer_state.opsMoveAfter>10) printer_state.opsMoveAfter=100; + extruder_select(current_extruder->id); + break; + case UI_ACTION_OPS_MINDISTANCE: + printer_state.opsMinDistance+=increment; + if(printer_state.opsMinDistance<0) printer_state.opsMinDistance=0; + else if(printer_state.opsMinDistance>10) printer_state.opsMinDistance=10; + extruder_select(current_extruder->id); + break; +#endif + case UI_ACTION_FEEDRATE_MULTIPLY: + { + int fr = printer_state.feedrateMultiply; + INCREMENT_MIN_MAX(fr,1,25,500); + change_feedrate_multiply(fr); + } + break; + case UI_ACTION_FLOWRATE_MULTIPLY: + { + INCREMENT_MIN_MAX(printer_state.extrudeMultiply,1,25,500); + OUT_P_I_LN("FlowrateMultiply:",printer_state.extrudeMultiply); + } + break; + case UI_ACTION_STEPPER_INACTIVE: + INCREMENT_MIN_MAX(stepper_inactive_time,60,0,9999); + break; + case UI_ACTION_MAX_INACTIVE: + INCREMENT_MIN_MAX(max_inactive_time,60,0,9999); + break; + case UI_ACTION_PRINT_ACCEL_X: + INCREMENT_MIN_MAX(max_acceleration_units_per_sq_second[0],100,0,10000); + update_ramps_parameter(); + break; + case UI_ACTION_PRINT_ACCEL_Y: + INCREMENT_MIN_MAX(max_acceleration_units_per_sq_second[1],100,0,10000); + update_ramps_parameter(); + break; + case UI_ACTION_PRINT_ACCEL_Z: + INCREMENT_MIN_MAX(max_acceleration_units_per_sq_second[2],100,0,10000); + update_ramps_parameter(); + break; + case UI_ACTION_MOVE_ACCEL_X: + INCREMENT_MIN_MAX(max_travel_acceleration_units_per_sq_second[0],100,0,10000); + update_ramps_parameter(); + break; + case UI_ACTION_MOVE_ACCEL_Y: + INCREMENT_MIN_MAX(max_travel_acceleration_units_per_sq_second[1],100,0,10000); + update_ramps_parameter(); + break; + case UI_ACTION_MOVE_ACCEL_Z: + INCREMENT_MIN_MAX(max_travel_acceleration_units_per_sq_second[2],100,0,10000); + update_ramps_parameter(); + break; + case UI_ACTION_MAX_JERK: + INCREMENT_MIN_MAX(printer_state.maxJerk,0.1,1,99.9); + break; + case UI_ACTION_MAX_ZJERK: + INCREMENT_MIN_MAX(printer_state.maxZJerk,0.1,0.1,99.9); + break; + case UI_ACTION_HOMING_FEEDRATE_X: + INCREMENT_MIN_MAX(homing_feedrate[0],1,5,1000); + break; + case UI_ACTION_HOMING_FEEDRATE_Y: + INCREMENT_MIN_MAX(homing_feedrate[1],1,5,1000); + break; + case UI_ACTION_HOMING_FEEDRATE_Z: + INCREMENT_MIN_MAX(homing_feedrate[2],1,1,1000); + break; + case UI_ACTION_MAX_FEEDRATE_X: + INCREMENT_MIN_MAX(max_feedrate[0],1,1,1000); + break; + case UI_ACTION_MAX_FEEDRATE_Y: + INCREMENT_MIN_MAX(max_feedrate[1],1,1,1000); + break; + case UI_ACTION_MAX_FEEDRATE_Z: + INCREMENT_MIN_MAX(max_feedrate[2],1,1,1000); + break; + case UI_ACTION_STEPS_X: + INCREMENT_MIN_MAX(axis_steps_per_unit[0],0.1,0,999); + update_ramps_parameter(); + break; + case UI_ACTION_STEPS_Y: + INCREMENT_MIN_MAX(axis_steps_per_unit[1],0.1,0,999); + update_ramps_parameter(); + break; + case UI_ACTION_STEPS_Z: + INCREMENT_MIN_MAX(axis_steps_per_unit[2],0.1,0,999); + update_ramps_parameter(); + break; + case UI_ACTION_BAUDRATE: +#if EEPROM_MODE!=0 + { + char p=0; + long rate; + do { + rate = pgm_read_dword(&(baudrates[p])); + if(rate==baudrate) break; + p++; + } while(rate!=0); + if(rate==0) p-=2; + p+=increment; + if(p<0) p = 0; + rate = pgm_read_dword(&(baudrates[p])); + if(rate==0) p--; + baudrate = pgm_read_dword(&(baudrates[p])); + } +#endif + break; +#ifdef TEMP_PID + case UI_ACTION_PID_PGAIN: + INCREMENT_MIN_MAX(current_extruder->tempControl.pidPGain,0.1,0,200); + break; + case UI_ACTION_PID_IGAIN: + INCREMENT_MIN_MAX(current_extruder->tempControl.pidIGain,0.01,0,100); + extruder_select(current_extruder->id); + break; + case UI_ACTION_PID_DGAIN: + INCREMENT_MIN_MAX(current_extruder->tempControl.pidDGain,0.1,0,200); + break; + case UI_ACTION_DRIVE_MIN: + INCREMENT_MIN_MAX(current_extruder->tempControl.pidDriveMin,1,1,255); + break; + case UI_ACTION_DRIVE_MAX: + INCREMENT_MIN_MAX(current_extruder->tempControl.pidDriveMax,1,1,255); + break; + case UI_ACTION_PID_MAX: + INCREMENT_MIN_MAX(current_extruder->tempControl.pidMax,1,1,255); + break; +#endif + case UI_ACTION_X_OFFSET: + INCREMENT_MIN_MAX(current_extruder->xOffset,1,-99999,99999); + extruder_select(current_extruder->id); + break; + case UI_ACTION_Y_OFFSET: + INCREMENT_MIN_MAX(current_extruder->yOffset,1,-99999,99999); + extruder_select(current_extruder->id); + break; + case UI_ACTION_EXTR_STEPS: + INCREMENT_MIN_MAX(current_extruder->stepsPerMM,1,1,9999); + extruder_select(current_extruder->id); + break; + case UI_ACTION_EXTR_ACCELERATION: + INCREMENT_MIN_MAX(current_extruder->maxAcceleration,10,10,99999); + extruder_select(current_extruder->id); + break; + case UI_ACTION_EXTR_MAX_FEEDRATE: + INCREMENT_MIN_MAX(current_extruder->maxFeedrate,1,1,999); + extruder_select(current_extruder->id); + break; + case UI_ACTION_EXTR_START_FEEDRATE: + INCREMENT_MIN_MAX(current_extruder->maxStartFeedrate,1,1,999); + extruder_select(current_extruder->id); + break; + case UI_ACTION_EXTR_HEATMANAGER: + INCREMENT_MIN_MAX(current_extruder->tempControl.heatManager,1,0,1); + break; + case UI_ACTION_EXTR_WATCH_PERIOD: + INCREMENT_MIN_MAX(current_extruder->watchPeriod,1,0,999); + break; +#if RETRACT_DURING_HEATUP + case UI_ACTION_EXTR_WAIT_RETRACT_TEMP: + INCREMENT_MIN_MAX(current_extruder->waitRetractTemperature,1,100,UI_SET_MAX_EXTRUDER_TEMP); + break; + case UI_ACTION_EXTR_WAIT_RETRACT_UNITS: + INCREMENT_MIN_MAX(current_extruder->waitRetractUnits,1,0,99); + break; +#endif +#ifdef USE_ADVANCE +#ifdef ENABLE_QUADRATIC_ADVANCE + case UI_ACTION_ADVANCE_K: + INCREMENT_MIN_MAX(current_extruder->advanceK,1,0,200); + break; +#endif + case UI_ACTION_ADVANCE_L: + INCREMENT_MIN_MAX(current_extruder->advanceL,1,0,600); + break; +#endif + } +#if UI_AUTORETURN_TO_MENU_AFTER!=0 + ui_autoreturn_time=millis()+UI_AUTORETURN_TO_MENU_AFTER; +#endif +#endif +} + +void UIDisplay::finishAction(int action) { +} +// Actions are events from user input. Depending on the current state, each +// action can behave differently. Other actions do always the same like home, disable extruder etc. +void UIDisplay::executeAction(int action) { +#if UI_HAS_KEYS==1 + bool skipBeep = false; + if(action & UI_ACTION_TOPMENU) { // Go to start menu + action -= UI_ACTION_TOPMENU; + menuLevel = 0; + } + if(action>=2000 && action<3000) + { + setStatusP(ui_action); + } + else + switch(action) { + case UI_ACTION_OK: + okAction(); + skipBeep=true; // Prevent double beep + break; + case UI_ACTION_BACK: + if(menuLevel>0) menuLevel--; + activeAction = 0; + break; + case UI_ACTION_NEXT: + nextPreviousAction(1); + break; + case UI_ACTION_PREVIOUS: + nextPreviousAction(-1); + break; + case UI_ACTION_MENU_UP: + if(menuLevel>0) menuLevel--; + break; + case UI_ACTION_TOP_MENU: + menuLevel = 0; + break; + case UI_ACTION_EMERGENCY_STOP: + emergencyStop(); + break; + case UI_ACTION_HOME_ALL: + home_axis(true,true,true); + printPosition(); + break; + case UI_ACTION_HOME_X: + home_axis(true,false,false); + printPosition(); + break; + case UI_ACTION_HOME_Y: + home_axis(false,true,false); + printPosition(); + break; + case UI_ACTION_HOME_Z: + home_axis(false,false,true); + printPosition(); + break; + case UI_ACTION_SET_ORIGIN: + printer_state.currentPositionSteps[0] = -printer_state.offsetX; + printer_state.currentPositionSteps[1] = -printer_state.offsetY; + printer_state.currentPositionSteps[2] = 0; + break; + case UI_ACTION_DEBUG_ECHO: + if(DEBUG_ECHO) debug_level-=1;else debug_level+=1; + break; + case UI_ACTION_DEBUG_INFO: + if(DEBUG_INFO) debug_level-=2;else debug_level+=2; + break; + case UI_ACTION_DEBUG_ERROR: + if(DEBUG_ERRORS) debug_level-=4;else debug_level+=4; + break; + case UI_ACTION_DEBUG_DRYRUN: + if(DEBUG_DRYRUN) debug_level-=8;else debug_level+=8; + if(DEBUG_DRYRUN) { // simulate movements without printing + extruder_set_temperature(0,0); +#if NUM_EXTRUDER>1 + extruder_set_temperature(0,1); +#endif +#if HAVE_HEATED_BED==true + heated_bed_set_temperature(0); +#endif + } + break; + case UI_ACTION_POWER: + break; + case UI_ACTION_PREHEAT_PLA: + UI_STATUS(UI_TEXT_PREHEAT_PLA); + extruder_set_temperature(UI_SET_PRESET_EXTRUDER_TEMP_PLA,0); +#if NUM_EXTRUDER>1 + extruder_set_temperature(UI_SET_PRESET_EXTRUDER_TEMP_PLA,1); +#endif +#if HAVE_HEATED_BED==true + heated_bed_set_temperature(UI_SET_PRESET_HEATED_BED_TEMP_PLA); +#endif + break; + case UI_ACTION_PREHEAT_ABS: + UI_STATUS(UI_TEXT_PREHEAT_ABS); + extruder_set_temperature(UI_SET_PRESET_EXTRUDER_TEMP_ABS,0); +#if NUM_EXTRUDER>1 + extruder_set_temperature(UI_SET_PRESET_EXTRUDER_TEMP_ABS,1); +#endif +#if HAVE_HEATED_BED==true + heated_bed_set_temperature(UI_SET_PRESET_HEATED_BED_TEMP_ABS); +#endif + break; + case UI_ACTION_COOLDOWN: + UI_STATUS(UI_TEXT_COOLDOWN); + extruder_set_temperature(0,0); +#if NUM_EXTRUDER>1 + extruder_set_temperature(0,1); +#endif +#if HAVE_HEATED_BED==true + heated_bed_set_temperature(0); +#endif + break; + case UI_ACTION_HEATED_BED_OFF: +#if HAVE_HEATED_BED==true + heated_bed_set_temperature(0); +#endif + break; + case UI_ACTION_EXTRUDER0_OFF: + extruder_set_temperature(0,0); + break; + case UI_ACTION_EXTRUDER1_OFF: + #if NUM_EXTRUDER>1 + extruder_set_temperature(0,1); + #endif + break; +#if USE_OPS==1 + case UI_ACTION_OPS_OFF: + printer_state.opsMode=0; + break; + case UI_ACTION_OPS_CLASSIC: + printer_state.opsMode=1; + break; + case UI_ACTION_OPS_FAST: + printer_state.opsMode=2; + break; + #endif + case UI_ACTION_DISABLE_STEPPER: + kill(true); + break; + case UI_ACTION_RESET_EXTRUDER: + printer_state.currentPositionSteps[3] = 0; + break; + case UI_ACTION_EXTRUDER_RELATIVE: + relative_mode_e=!relative_mode_e; + break; + case UI_ACTION_SELECT_EXTRUDER0: + extruder_select(0); + break; + case UI_ACTION_SELECT_EXTRUDER1: +#if NUM_EXTRUDER>1 + extruder_select(1); +#endif + break; +#if EEPROM_MODE!=0 + case UI_ACTION_STORE_EEPROM: + epr_data_to_eeprom(false); + pushMenu((void*)&ui_menu_eeprom_saved,false); + BEEP_LONG;skipBeep = true; + break; + case UI_ACTION_LOAD_EEPROM: + epr_eeprom_to_data(); + pushMenu((void*)&ui_menu_eeprom_loaded,false); + BEEP_LONG;skipBeep = true; + break; +#endif +#if SDSUPPORT + case UI_ACTION_SD_DELETE: + if(sd.sdactive){ + pushMenu((void*)&ui_menu_sd_fileselector,false); + } else { + UI_ERROR(UI_TEXT_NOSDCARD); + } + break; + case UI_ACTION_SD_PRINT: + if(sd.sdactive){ + pushMenu((void*)&ui_menu_sd_fileselector,false); + } + break; + case UI_ACTION_SD_PAUSE: + if(sd.sdmode){ + sd.sdmode = false; + } + break; + case UI_ACTION_SD_CONTINUE: + if(sd.sdactive){ + sd.sdmode = true; + } + break; + case UI_ACTION_SD_UNMOUNT: + sd.sdmode = false; + sd.sdactive = false; + break; + case UI_ACTION_SD_MOUNT: + sd.sdmode = false; + sd.initsd(); + break; + case UI_ACTION_MENU_SDCARD: + pushMenu((void*)&ui_menu_sd,false); + break; +#endif +#if FAN_PIN>-1 + case UI_ACTION_FAN_OFF: + set_fan_speed(0,false); + OUT_P_LN("Fanspeed:0"); + break; + case UI_ACTION_FAN_25: + set_fan_speed(64,false); + OUT_P_LN("Fanspeed:64"); + break; + case UI_ACTION_FAN_50: + set_fan_speed(128,false); + OUT_P_LN("Fanspeed:128"); + break; + case UI_ACTION_FAN_75: + set_fan_speed(192,false); + OUT_P_LN("Fanspeed:192"); + break; + case UI_ACTION_FAN_FULL: + set_fan_speed(255,false); + OUT_P_LN("Fanspeed:255"); + break; +#endif + case UI_ACTION_MENU_XPOS: + pushMenu((void*)&ui_menu_xpos,false); + break; + case UI_ACTION_MENU_YPOS: + pushMenu((void*)&ui_menu_ypos,false); + break; + case UI_ACTION_MENU_ZPOS: + pushMenu((void*)&ui_menu_zpos,false); + break; + case UI_ACTION_MENU_XPOSFAST: + pushMenu((void*)&ui_menu_xpos_fast,false); + break; + case UI_ACTION_MENU_YPOSFAST: + pushMenu((void*)&ui_menu_ypos_fast,false); + break; + case UI_ACTION_MENU_ZPOSFAST: + pushMenu((void*)&ui_menu_zpos_fast,false); + break; + case UI_ACTION_MENU_QUICKSETTINGS: + pushMenu((void*)&ui_menu_quick,false); + break; + case UI_ACTION_MENU_EXTRUDER: + pushMenu((void*)&ui_menu_extruder,false); + break; + case UI_ACTION_MENU_POSITIONS: + pushMenu((void*)&ui_menu_positions,false); + break; +#ifdef UI_USERMENU1 + case UI_ACTION_SHOW_USERMENU1: + pushMenu((void*)&UI_USERMENU1,false); + break; +#endif +#ifdef UI_USERMENU2 + case UI_ACTION_SHOW_USERMENU2: + pushMenu((void*)&UI_USERMENU2,false); + break; +#endif +#ifdef UI_USERMENU3 + case UI_ACTION_SHOW_USERMENU3: + pushMenu((void*)&UI_USERMENU3,false); + break; +#endif +#ifdef UI_USERMENU4 + case UI_ACTION_SHOW_USERMENU4: + pushMenu((void*)&UI_USERMENU4,false); + break; +#endif +#ifdef UI_USERMENU5 + case UI_ACTION_SHOW_USERMENU5: + pushMenu((void*)&UI_USERMENU5,false); + break; +#endif +#ifdef UI_USERMENU6 + case UI_ACTION_SHOW_USERMENU6: + pushMenu((void*)&UI_USERMENU6,false); + break; +#endif +#ifdef UI_USERMENU7 + case UI_ACTION_SHOW_USERMENU7: + pushMenu((void*)&UI_USERMENU7,false); + break; +#endif +#ifdef UI_USERMENU8 + case UI_ACTION_SHOW_USERMENU8: + pushMenu((void*)&UI_USERMENU8,false); + break; +#endif +#ifdef UI_USERMENU9 + case UI_ACTION_SHOW_USERMENU9: + pushMenu((void*)&UI_USERMENU9,false); + break; +#endif +#ifdef UI_USERMENU10 + case UI_ACTION_SHOW_USERMENU10: + pushMenu((void*)&UI_USERMENU10,false); + break; +#endif + case UI_ACTION_X_UP: + move_steps(axis_steps_per_unit[0],0,0,0,homing_feedrate[0],false,true); + break; + case UI_ACTION_X_DOWN: + move_steps(-axis_steps_per_unit[0],0,0,0,homing_feedrate[0],false,true); + break; + case UI_ACTION_Y_UP: + move_steps(0,axis_steps_per_unit[1],0,0,homing_feedrate[1],false,true); + break; + case UI_ACTION_Y_DOWN: + move_steps(0,-axis_steps_per_unit[1],0,0,homing_feedrate[1],false,true); + break; + case UI_ACTION_Z_UP: + move_steps(0,0,axis_steps_per_unit[2],0,homing_feedrate[2],false,true); + break; + case UI_ACTION_Z_DOWN: + move_steps(0,0,-axis_steps_per_unit[2],0,homing_feedrate[2],false,true); + break; + case UI_ACTION_EXTRUDER_UP: + move_steps(0,0,0,axis_steps_per_unit[3],UI_SET_EXTRUDER_FEEDRATE,false,true); + break; + case UI_ACTION_EXTRUDER_DOWN: + move_steps(0,0,0,-axis_steps_per_unit[3],UI_SET_EXTRUDER_FEEDRATE,false,true); + break; + case UI_ACTION_EXTRUDER_TEMP_UP: { + int tmp = (int)(current_extruder->tempControl.targetTemperatureC)+1; + if(tmp==1) tmp = UI_SET_MIN_EXTRUDER_TEMP; + else if(tmp>UI_SET_MAX_EXTRUDER_TEMP) tmp = UI_SET_MAX_EXTRUDER_TEMP; + extruder_set_temperature(tmp,current_extruder->id); + } + break; + case UI_ACTION_EXTRUDER_TEMP_DOWN: { + int tmp = (int)(current_extruder->tempControl.targetTemperatureC)-1; + if(tmpid); + } + break; + case UI_ACTION_HEATED_BED_UP: +#if HAVE_HEATED_BED==true + { + int tmp = (int)heatedBedController.targetTemperatureC+1; + if(tmp==1) tmp = UI_SET_MIN_HEATED_BED_TEMP; + else if(tmp>UI_SET_MAX_HEATED_BED_TEMP) tmp = UI_SET_MAX_HEATED_BED_TEMP; + heated_bed_set_temperature(tmp); + } +#endif + break; + case UI_ACTION_SHOW_MEASUREMENT: +#ifdef STEP_COUNTER + { + out.print_float_P(PSTR("Measure/delta ="),printer_state.countZSteps * inv_axis_steps_per_unit[2]); + } +#endif + break; + case UI_ACTION_RESET_MEASUREMENT: +#ifdef STEP_COUNTER + { + printer_state.countZSteps = 0; + out.println_P(PSTR("Measurement reset.")); + } +#endif + break; + case UI_ACTION_SET_MEASURED_ORIGIN: +#ifdef STEP_COUNTER + { + if (printer_state.countZSteps < 0) + printer_state.countZSteps = -printer_state.countZSteps; + printer_state.zLength = inv_axis_steps_per_unit[2] * printer_state.countZSteps; + printer_state.zMaxSteps = printer_state.countZSteps; + for (byte i=0; i<3; i++) { + printer_state.currentPositionSteps[i] = 0; + } + calculate_delta(printer_state.currentPositionSteps, printer_state.currentDeltaPositionSteps); + out.println_P(PSTR("Measured origin set. Measurement reset.")); +#if EEPROM_MODE!=0 + epr_data_to_eeprom(false); + out.println_P(PSTR("EEPROM updated")); +#endif + } +#endif + case UI_ACTION_SET_P1: +#ifdef SOFTWARE_LEVELING + for (byte i=0; i<3; i++) { + printer_state.levelingP1[i] = printer_state.currentPositionSteps[i]; + } +#endif + break; + case UI_ACTION_SET_P2: +#ifdef SOFTWARE_LEVELING + for (byte i=0; i<3; i++) { + printer_state.levelingP2[i] = printer_state.currentPositionSteps[i]; + } +#endif + break; + case UI_ACTION_SET_P3: +#ifdef SOFTWARE_LEVELING + for (byte i=0; i<3; i++) { + printer_state.levelingP3[i] = printer_state.currentPositionSteps[i]; + } +#endif + break; + case UI_ACTION_CALC_LEVEL: +#ifdef SOFTWARE_LEVELING + long factors[4]; + calculate_plane(factors, printer_state.levelingP1, printer_state.levelingP2, printer_state.levelingP3); + out.println_P(PSTR("Leveling calc:")); + out.println_float_P(PSTR("Tower 1:"), calc_zoffset(factors, DELTA_TOWER1_X_STEPS, DELTA_TOWER1_Y_STEPS) * inv_axis_steps_per_unit[0]); + out.println_float_P(PSTR("Tower 2:"), calc_zoffset(factors, DELTA_TOWER2_X_STEPS, DELTA_TOWER2_Y_STEPS) * inv_axis_steps_per_unit[1]); + out.println_float_P(PSTR("Tower 3:"), calc_zoffset(factors, DELTA_TOWER3_X_STEPS, DELTA_TOWER3_Y_STEPS) * inv_axis_steps_per_unit[2]); +#endif + break; + case UI_ACTION_HEATED_BED_DOWN: +#if HAVE_HEATED_BED==true + { + int tmp = (int)heatedBedController.targetTemperatureC-1; + if(tmp-1 + WRITE(EXT0_HEATER_PIN,0); +#endif +#if defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN>-1 + WRITE(EXT1_HEATER_PIN,0); +#endif +#if defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN>-1 + WRITE(EXT2_HEATER_PIN,0); +#endif +#if FAN_PIN>-1 + WRITE(FAN_PIN,0); +#endif + resetFunc(); + + while(1) {} + + break; + case UI_ACTION_RESET: + resetFunc(); + break; + case UI_ACTION_PAUSE: + OUT_P_LN("RequestPause:"); + break; + } + refreshPage(); + if(!skipBeep) + BEEP_SHORT +#if UI_AUTORETURN_TO_MENU_AFTER!=0 + ui_autoreturn_time=millis()+UI_AUTORETURN_TO_MENU_AFTER; +#endif +#endif +} +void UIDisplay::mediumAction() { +#if UI_HAS_I2C_ENCODER>0 + ui_check_slow_encoder(); +#endif +} +void UIDisplay::slowAction() { + unsigned long time = millis(); + byte refresh=0; +#if UI_HAS_KEYS==1 + // Update key buffer + cli(); + if((flags & 9)==0) { + flags|=8; + sei(); + int nextAction = 0; + ui_check_slow_keys(nextAction); + if(lastButtonAction!=nextAction) { + lastButtonStart = time; + lastButtonAction = nextAction; + cli(); + flags|=2; // Mark slow action + } + cli(); + flags-=8; + } + cli(); + if((flags & 4)==0) { + flags |= 4; + // Reset click encoder + cli(); + char epos = encoderPos; + encoderPos=0; + sei(); + if(epos) { + nextPreviousAction(epos); + BEEP_SHORT + refresh=1; + } + if(lastAction!=lastButtonAction) { + if(lastButtonAction==0) { + if(lastAction>=2000 && lastAction<3000) + { + statusMsg[0] = 0; + } + lastAction = 0; + cli(); + flags &= ~3; + } else if(time-lastButtonStart>UI_KEY_BOUNCETIME) { // New key pressed + lastAction = lastButtonAction; + executeAction(lastAction); + nextRepeat = time+UI_KEY_FIRST_REPEAT; + repeatDuration = UI_KEY_FIRST_REPEAT; + } + } else if(lastAction<1000 && lastAction) { // Repeatable key + if(time-nextRepeat<10000) { + executeAction(lastAction); + repeatDuration -=UI_KEY_REDUCE_REPEAT; + if(repeatDuration0 && ui_autoreturn_time4000) { + if(time-lastSwitch>UI_PAGES_DURATION) { + lastSwitch = time; +#if !defined(UI_DISABLE_AUTO_PAGESWITCH) || !UI_DISABLE_AUTO_PAGESWITCH + menuPos[0]++; + if(menuPos[0]>=UI_NUM_PAGES) + menuPos[0]=0; +#endif + refresh = 1; + } else if(time-lastRefresh>=1000) refresh=1; + } else if(time-lastRefresh>=1000) { + UIMenu *men = (UIMenu*)menu[menuLevel]; + byte mtype = pgm_read_byte((void*)&(men->menuType)); + if(mtype!=1) + refresh=1; + } + if(refresh) { + refreshPage(); + lastRefresh = time; + } +} +void UIDisplay::fastAction() { +#if UI_HAS_KEYS==1 + // Check keys + cli(); + if((flags & 10)==0) { + flags |= 8; + sei(); + int nextAction = 0; + ui_check_keys(nextAction); + if(lastButtonAction!=nextAction) { + lastButtonStart = millis(); + lastButtonAction = nextAction; + cli(); + flags|=1; + } + cli(); + flags-=8; + } + sei(); +#endif +} + +#endif + diff --git a/Repetier/ui.h b/Repetier/ui.h new file mode 100644 index 0000000..8a59db5 --- /dev/null +++ b/Repetier/ui.h @@ -0,0 +1,695 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + +*/ + +#ifndef _ui_h +#define _ui_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#define COMPAT_PRE1 +#endif +#include +#include "gcode.h" + +// ---------------------------------------------------------------------------- +// Action codes +// 1-999 : Autorepeat +// 1000-1999 : Execute +// 2000-2999 : Write code +// 4000-4999 : Show menu +// Add UI_ACTION_TOPMENU to show a menu as top menu +// ---------------------------------------------------------------------------- + +#define UI_ACTION_TOPMENU 8192 + +#define UI_ACTION_NEXT 1 +#define UI_ACTION_PREVIOUS 2 + +#define UI_ACTION_X_UP 100 +#define UI_ACTION_X_DOWN 101 +#define UI_ACTION_Y_UP 102 +#define UI_ACTION_Y_DOWN 103 +#define UI_ACTION_Z_UP 104 +#define UI_ACTION_Z_DOWN 105 +#define UI_ACTION_EXTRUDER_UP 106 +#define UI_ACTION_EXTRUDER_DOWN 107 +#define UI_ACTION_EXTRUDER_TEMP_UP 108 +#define UI_ACTION_EXTRUDER_TEMP_DOWN 109 +#define UI_ACTION_HEATED_BED_UP 110 +#define UI_ACTION_HEATED_BED_DOWN 111 +#define UI_ACTION_FAN_UP 112 +#define UI_ACTION_FAN_DOWN 113 + +#define UI_ACTION_DUMMY 10000 +#define UI_ACTION_BACK 1000 +#define UI_ACTION_OK 1001 +#define UI_ACTION_MENU_UP 1002 +#define UI_ACTION_TOP_MENU 1003 +#define UI_ACTION_EMERGENCY_STOP 1004 +#define UI_ACTION_XPOSITION 1005 +#define UI_ACTION_YPOSITION 1006 +#define UI_ACTION_ZPOSITION 1007 +#define UI_ACTION_EPOSITION 1008 +#define UI_ACTION_BED_TEMP 1009 +#define UI_ACTION_EXTRUDER_TEMP 1010 +#define UI_ACTION_SD_DELETE 1012 +#define UI_ACTION_SD_PRINT 1013 +#define UI_ACTION_SD_PAUSE 1014 +#define UI_ACTION_SD_CONTINUE 1015 +#define UI_ACTION_SD_UNMOUNT 1016 +#define UI_ACTION_SD_MOUNT 1017 +#define UI_ACTION_XPOSITION_FAST 1018 +#define UI_ACTION_YPOSITION_FAST 1019 +#define UI_ACTION_ZPOSITION_FAST 1020 +#define UI_ACTION_HOME_ALL 1021 +#define UI_ACTION_HOME_X 1022 +#define UI_ACTION_HOME_Y 1023 +#define UI_ACTION_HOME_Z 1024 +#define UI_ACTION_SELECT_EXTRUDER1 1025 +#define UI_ACTION_OPS_RETRACTDISTANCE 1026 +#define UI_ACTION_OPS_BACKLASH 1027 +#define UI_ACTION_OPS_MOVE_AFTER 1028 +#define UI_ACTION_OPS_MINDISTANCE 1029 +#define UI_ACTION_STORE_EEPROM 1030 +#define UI_ACTION_LOAD_EEPROM 1031 +#define UI_ACTION_PRINT_ACCEL_X 1032 +#define UI_ACTION_PRINT_ACCEL_Y 1033 +#define UI_ACTION_PRINT_ACCEL_Z 1034 +#define UI_ACTION_MOVE_ACCEL_X 1035 +#define UI_ACTION_MOVE_ACCEL_Y 1036 +#define UI_ACTION_MOVE_ACCEL_Z 1037 +#define UI_ACTION_MAX_JERK 1038 +#define UI_ACTION_MAX_ZJERK 1039 +#define UI_ACTION_BAUDRATE 1040 +#define UI_ACTION_HOMING_FEEDRATE_X 1041 +#define UI_ACTION_HOMING_FEEDRATE_Y 1042 +#define UI_ACTION_HOMING_FEEDRATE_Z 1043 +#define UI_ACTION_MAX_FEEDRATE_X 1044 +#define UI_ACTION_MAX_FEEDRATE_Y 1045 +#define UI_ACTION_MAX_FEEDRATE_Z 1046 +#define UI_ACTION_STEPS_X 1047 +#define UI_ACTION_STEPS_Y 1048 +#define UI_ACTION_STEPS_Z 1049 +#define UI_ACTION_FAN_OFF 1050 +#define UI_ACTION_FAN_25 1051 +#define UI_ACTION_FAN_50 1052 +#define UI_ACTION_FAN_75 1053 +#define UI_ACTION_FAN_FULL 1054 +#define UI_ACTION_FEEDRATE_MULTIPLY 1055 +#define UI_ACTION_STEPPER_INACTIVE 1056 +#define UI_ACTION_MAX_INACTIVE 1057 +#define UI_ACTION_PID_PGAIN 1058 +#define UI_ACTION_PID_IGAIN 1059 +#define UI_ACTION_PID_DGAIN 1060 +#define UI_ACTION_DRIVE_MIN 1061 +#define UI_ACTION_DRIVE_MAX 1062 +#define UI_ACTION_X_OFFSET 1063 +#define UI_ACTION_Y_OFFSET 1064 +#define UI_ACTION_EXTR_STEPS 1065 +#define UI_ACTION_EXTR_ACCELERATION 1066 +#define UI_ACTION_EXTR_MAX_FEEDRATE 1067 +#define UI_ACTION_EXTR_START_FEEDRATE 1068 +#define UI_ACTION_EXTR_HEATMANAGER 1069 +#define UI_ACTION_EXTR_WATCH_PERIOD 1070 +#define UI_ACTION_PID_MAX 1071 +#define UI_ACTION_ADVANCE_K 1072 +#define UI_ACTION_SET_ORIGIN 1073 +#define UI_ACTION_DEBUG_ECHO 1074 +#define UI_ACTION_DEBUG_INFO 1075 +#define UI_ACTION_DEBUG_ERROR 1076 +#define UI_ACTION_DEBUG_DRYRUN 1077 +#define UI_ACTION_POWER 1078 +#define UI_ACTION_PREHEAT_PLA 1079 +#define UI_ACTION_COOLDOWN 1080 +#define UI_ACTION_HEATED_BED_OFF 1081 +#define UI_ACTION_EXTRUDER0_OFF 1082 +#define UI_ACTION_EXTRUDER1_OFF 1083 +#define UI_ACTION_HEATED_BED_TEMP 1084 +#define UI_ACTION_EXTRUDER0_TEMP 1085 +#define UI_ACTION_EXTRUDER1_TEMP 1086 +#define UI_ACTION_OPS_OFF 1087 +#define UI_ACTION_OPS_CLASSIC 1088 +#define UI_ACTION_OPS_FAST 1089 +#define UI_ACTION_DISABLE_STEPPER 1090 +#define UI_ACTION_RESET_EXTRUDER 1091 +#define UI_ACTION_EXTRUDER_RELATIVE 1092 +#define UI_ACTION_SELECT_EXTRUDER0 1093 +#define UI_ACTION_ADVANCE_L 1094 +#define UI_ACTION_PREHEAT_ABS 1095 +#define UI_ACTION_FLOWRATE_MULTIPLY 1096 +#define UI_ACTION_KILL 1097 +#define UI_ACTION_RESET 1098 +#define UI_ACTION_PAUSE 1099 +#define UI_ACTION_EXTR_WAIT_RETRACT_TEMP 1100 +#define UI_ACTION_EXTR_WAIT_RETRACT_UNITS 1101 + +#define UI_ACTION_MENU_XPOS 4000 +#define UI_ACTION_MENU_YPOS 4001 +#define UI_ACTION_MENU_ZPOS 4002 +#define UI_ACTION_MENU_XPOSFAST 4003 +#define UI_ACTION_MENU_YPOSFAST 4004 +#define UI_ACTION_MENU_ZPOSFAST 4005 +#define UI_ACTION_MENU_SDCARD 4006 +#define UI_ACTION_MENU_QUICKSETTINGS 4007 +#define UI_ACTION_MENU_EXTRUDER 4008 +#define UI_ACTION_MENU_POSITIONS 4009 +#define UI_ACTION_SHOW_MEASUREMENT 4010 +#define UI_ACTION_RESET_MEASUREMENT 4011 +#define UI_ACTION_SET_MEASURED_ORIGIN 4012 +#define UI_ACTION_SET_P1 4013 +#define UI_ACTION_SET_P2 4014 +#define UI_ACTION_SET_P3 4015 +#define UI_ACTION_CALC_LEVEL 4016 + +#define UI_ACTION_SHOW_USERMENU1 4101 +#define UI_ACTION_SHOW_USERMENU2 4102 +#define UI_ACTION_SHOW_USERMENU3 4103 +#define UI_ACTION_SHOW_USERMENU4 4104 +#define UI_ACTION_SHOW_USERMENU5 4105 +#define UI_ACTION_SHOW_USERMENU6 4106 +#define UI_ACTION_SHOW_USERMENU7 4107 +#define UI_ACTION_SHOW_USERMENU8 4108 +#define UI_ACTION_SHOW_USERMENU9 4109 +#define UI_ACTION_SHOW_USERMENU10 4110 + +// Load basic language definition to make sure all values are defined +#include "uilang.h" + +#include "Configuration.h" +#include +#include "fastio.h" + +typedef struct { + const char *text; // Menu text + unsigned char menuType; // 0 = Info, 1 = Headline, 2 = submenu ref, 3 = direct action command, 4 = modify action command + unsigned int action; +} const UIMenuEntry; + +typedef struct { + // 0 = info page + // 1 = file selector + // 2 = submenu + // 3 = modififaction menu + unsigned char menuType; + int id; // Type of modification + int numEntries; + const UIMenuEntry * const * entries; +} const UIMenu; +extern const int8_t encoder_table[16] PROGMEM ; + +//#ifdef COMPILE_I2C_DRIVER + +/************************************************************************* +* Title: C include file for the I2C master interface +* (i2cmaster.S or twimaster.c) +* Author: Peter Fleury http://jump.to/fleury +* File: $Id: i2cmaster.h,v 1.10 2005/03/06 22:39:57 Peter Exp $ +* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3 +* Target: any AVR device +* Usage: see Doxygen manual +**************************************************************************/ + +#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304 +#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !" +#endif + +#include + +/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */ +#define I2C_READ 1 +/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */ +#define I2C_WRITE 0 + +/** + @brief Terminates the data transfer and releases the I2C bus + @param void + @return none + */ +extern void i2c_stop(void); +/** + @brief Issues a start condition and sends address and transfer direction + + @param addr address and transfer direction of I2C device + @retval 0 device accessible + @retval 1 failed to access device + */ +extern unsigned char i2c_start(unsigned char addr); +/** + @brief Issues a start condition and sends address and transfer direction + + If device is busy, use ack polling to wait until device ready + @param addr address and transfer direction of I2C device + @return none + */ +extern void i2c_start_wait(unsigned char addr); +/** + @brief Send one byte to I2C device + @param data byte to be transfered + @retval 0 write successful + @retval 1 write failed + */ +extern unsigned char i2c_write(unsigned char data); +/** + @brief read one byte from the I2C device, request more data from device + @return byte read from I2C device + */ +extern unsigned char i2c_readAck(void); +/** + @brief read one byte from the I2C device, read is followed by a stop condition + @return byte read from I2C device + */ +extern unsigned char i2c_readNak(void); +/** + @brief read one byte from the I2C device + + Implemented as a macro, which calls either i2c_readAck or i2c_readNak + + @param ack 1 send ack, request more data from device
+ 0 send nak, read is followed by a stop condition + @return byte read from I2C device + */ +extern unsigned char i2c_read(unsigned char ack); +#define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNak(); +/**@}*/ + + + +//extern const int matrixActions[] PROGMEM; +// Key codes +#define UI_KEYS_INIT_CLICKENCODER_LOW(pinA,pinB) SET_INPUT(pinA);SET_INPUT(pinB); WRITE(pinA,HIGH);WRITE(pinB,HIGH); +#define UI_KEYS_INIT_BUTTON_LOW(pin) SET_INPUT(pin);WRITE(pin,HIGH); +#define UI_KEYS_INIT_CLICKENCODER_HIGH(pinA,pinB) SET_INPUT(pinA);SET_INPUT(pinB); WRITE(pinA,LOW);WRITE(pinB,LOW); +#define UI_KEYS_INIT_BUTTON_HIGH(pin) SET_INPUT(pin);WRITE(pin,LOW); + +#define UI_KEYS_CLICKENCODER_LOW(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (!READ(pinA)) uid.encoderLast |=2;if (!READ(pinB)) uid.encoderLast |=1; uid.encoderPos += pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_CLICKENCODER_LOW_REV(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (!READ(pinA)) uid.encoderLast |=2;if (!READ(pinB)) uid.encoderLast |=1; uid.encoderPos -= pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_BUTTON_LOW(pin,action_) if(READ(pin)==0) action=action_; +#define UI_KEYS_CLICKENCODER_HIGH(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (READ(pinA)) uid.encoderLast |=2;if (READ(pinB)) uid.encoderLast |=1; uid.encoderPos += pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_CLICKENCODER_HIGH_REV(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (READ(pinA)) uid.encoderLast |=2;if (READ(pinB)) uid.encoderLast |=1; uid.encoderPos -= pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_BUTTON_HIGH(pin,action_) if(READ(pin)!=0) action=action_; +#define UI_KEYS_INIT_MATRIX(r1,r2,r3,r4,c1,c2,c3,c4) if(c1>=0){SET_INPUT(c1);WRITE(c1,HIGH);}if(c2>=0){SET_INPUT(c2);WRITE(c2,HIGH);}if(c3>=0){SET_INPUT(c3);WRITE(c3,HIGH);}\ + if(c4>=0) {SET_INPUT(c4);WRITE(c4,HIGH);}if(r1>=0)SET_OUTPUT(r1);if(r2>=0)SET_OUTPUT(r2);if(r3>=0)SET_OUTPUT(r3);if(r4>=0)SET_OUTPUT(r4);\ + if(r1>=0)WRITE(r1,LOW);if(r2>=0)WRITE(r2,LOW);if(r3>=0)WRITE(r3,LOW);if(r4>=0)WRITE(r4,LOW); +// out.print_int_P(PSTR("r4=>c1:"),READ(c1));out.print_int_P(PSTR(" c2:"),READ(c2));out.print_int_P(PSTR(" c3:"),READ(c3));out.println_int_P(PSTR(" c4:"),READ(c4)); +#define UI_KEYS_MATRIX(r1,r2,r3,r4,c1,c2,c3,c4) {byte r = (c1>=0?READ(c1):0) && (c2>=0?READ(c2):0) && (c3>=0?READ(c3):0) && (c4>=0?READ(c4):0);\ + if(!r) {\ + r = 255;\ + if(r2>=0)WRITE(r2,HIGH);if(r3>=0)WRITE(r3,HIGH);if(r4>=0)WRITE(r4,HIGH);\ + if(r1>=0) {\ + asm volatile ("nop\nnop\nnop\nnop\nnop");\ + if(!((c1>=0?READ(c1):1) && (c2>=0?READ(c2):1) && (c3>=0?READ(c3):1) && (c4>=0?READ(c4):1))) r = 0;\ + else WRITE(r1,HIGH);\ + }\ + if(r==255 && r2>=0) {\ + WRITE(r2,LOW);asm volatile ("nop\nnop\nnop\nnop\nnop");\ + if(!((c1>=0?READ(c1):1) && (c2>=0?READ(c2):1) && (c3>=0?READ(c3):1) && (c4>=0?READ(c4):1))) r = 4;\ + else WRITE(r2,HIGH);\ + }\ + if(r==255 && r3>=0) {\ + WRITE(r3,LOW);asm volatile ("nop\nnop\nnop\nnop\nnop");\ + if(!((c1>=0?READ(c1):0) && (c2>=0?READ(c2):1) && (c3>=0?READ(c3):1) && (c4>=0?READ(c4):1))) r = 8;\ + else WRITE(r3,HIGH);\ + }\ + if(r==255 && r4>=0) {\ + WRITE(r4,LOW);asm volatile ("nop\nnop\nnop\nnop\nnop");\ + if(!((c1>=0?READ(c1):1) && (c2>=0?READ(c2):1) && (c3>=0?READ(c3):1) && (c4>=0?READ(c4):1))) r = 12;\ + else WRITE(r4,HIGH);\ + }\ + if(c2>=0 && !READ(c2)) r+=1;\ + else if(c3>=0 && !READ(c3)) r+=2;\ + else if(c4>=0 && !READ(c4)) r+=3;\ + if(r<16) {action = pgm_read_word(&(matrixActions[r]));}\ + }if(r1>=0)WRITE(r1,LOW);if(r2>=0)WRITE(r2,LOW);if(r3>=0)WRITE(r3,LOW);if(r4>=0)WRITE(r4,LOW);} +// I2C keymask tests +#define UI_KEYS_I2C_CLICKENCODER_LOW(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (!(keymask & pinA)) uid.encoderLast |=2;if (!(keymask & pinB)) uid.encoderLast |=1; uid.encoderPos += pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_I2C_CLICKENCODER_LOW_REV(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (!(keymask & pinA)) uid.encoderLast |=2;if (!(keymask & pinB)) uid.encoderLast |=1; uid.encoderPos -= pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_I2C_BUTTON_LOW(pin,action_) if((keymask & pin)==0) action=action_; +#define UI_KEYS_I2C_CLICKENCODER_HIGH(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (keymask & pinA) uid.encoderLast |=2;if (keymask & pinB) uid.encoderLast |=1; uid.encoderPos += pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_I2C_CLICKENCODER_HIGH_REV(pinA,pinB) uid.encoderLast = (uid.encoderLast << 2) & 0x0F;if (keymask & pinA) uid.encoderLast |=2;if (keymask & pinB) uid.encoderLast |=1; uid.encoderPos -= pgm_read_byte(&encoder_table[uid.encoderLast]); +#define UI_KEYS_I2C_BUTTON_HIGH(pin,action_) if((pin & keymask)!=0) action=action_; + +#define UI_STRING(name,text) const char PROGMEM name[] = text; + +#define UI_PAGE4(name,row1,row2,row3,row4) UI_STRING(name ## _1txt,row1);UI_STRING(name ## _2txt,row2);UI_STRING(name ## _3txt,row3);UI_STRING(name ## _4txt,row4);\ + UIMenuEntry name ## _1 PROGMEM ={name ## _1txt,0,0};\ + UIMenuEntry name ## _2 PROGMEM ={name ## _2txt,0,0};\ + UIMenuEntry name ## _3 PROGMEM ={name ## _3txt,0,0};\ + UIMenuEntry name ## _4 PROGMEM ={name ## _4txt,0,0};\ + const UIMenuEntry * const name ## _entries [] PROGMEM = {&name ## _1,&name ## _2,&name ## _3,&name ## _4};\ + const UIMenu name PROGMEM = {0,0,4,name ## _entries}; +#define UI_PAGE2(name,row1,row2) UI_STRING(name ## _1txt,row1);UI_STRING(name ## _2txt,row2);\ + UIMenuEntry name ## _1 PROGMEM ={name ## _1txt,0,0};\ + UIMenuEntry name ## _2 PROGMEM ={name ## _2txt,0,0};\ + const UIMenuEntry * const name ## _entries[] PROGMEM = {&name ## _1,&name ## _2};\ + const UIMenu name PROGMEM = {0,0,2,name ## _entries}; +#define UI_MENU_ACTION4C(name,action,rows) UI_MENU_ACTION4(name,action,rows) +#define UI_MENU_ACTION2C(name,action,rows) UI_MENU_ACTION2(name,action,rows) +#define UI_MENU_ACTION4(name,action,row1,row2,row3,row4) UI_STRING(name ## _1txt,row1);UI_STRING(name ## _2txt,row2);UI_STRING(name ## _3txt,row3);UI_STRING(name ## _4txt,row4);\ + UIMenuEntry name ## _1 PROGMEM ={name ## _1txt,0,0};\ + UIMenuEntry name ## _2 PROGMEM ={name ## _2txt,0,0};\ + UIMenuEntry name ## _3 PROGMEM ={name ## _3txt,0,0};\ + UIMenuEntry name ## _4 PROGMEM ={name ## _4txt,0,0};\ + const UIMenuEntry * const name ## _entries[] PROGMEM = {&name ## _1,&name ## _2,&name ## _3,&name ## _4};\ + const UIMenu name PROGMEM = {3,action,4,name ## _entries}; +#define UI_MENU_ACTION2(name,action,row1,row2) UI_STRING(name ## _1txt,row1);UI_STRING(name ## _2txt,row2);\ + UIMenuEntry name ## _1 PROGMEM ={name ## _1txt,0,0};\ + UIMenuEntry name ## _2 PROGMEM ={name ## _2txt,0,0};\ + const UIMenuEntry * const name ## _entries[] PROGMEM = {&name ## _1,&name ## _2};\ + const UIMenu name PROGMEM = {3,action,2,name ## _entries}; +#define UI_MENU_HEADLINE(name,text) UI_STRING(name ## _txt,text);UIMenuEntry name PROGMEM = {name ## _txt,1,0}; +#define UI_MENU_CHANGEACTION(name,row,action) UI_STRING(name ## _txt,row);UIMenuEntry name PROGMEM = {name ## _txt,4,action}; +#define UI_MENU_ACTIONCOMMAND(name,row,action) UI_STRING(name ## _txt,row);UIMenuEntry name PROGMEM = {name ## _txt,3,action}; +#define UI_MENU_ACTIONSELECTOR(name,row,entries) UI_STRING(name ## _txt,row);UIMenuEntry name PROGMEM = {name ## _txt,2,(unsigned int)&entries}; +#define UI_MENU_SUBMENU(name,row,entries) UI_STRING(name ## _txt,row);UIMenuEntry name PROGMEM = {name ## _txt,2,(unsigned int)&entries}; +#define UI_MENU(name,items,itemsCnt) const UIMenuEntry * const name ## _entries[] PROGMEM = items;const UIMenu name PROGMEM = {2,0,itemsCnt,name ## _entries} +#define UI_MENU_FILESELECT(name,items,itemsCnt) const UIMenuEntry *name ## _entries[] PROGMEM = items;const UIMenu name PROGMEM = {1,0,itemsCnt,name ## _entries} + +#if FEATURE_CONTROLLER==2 // reprapdiscount smartcontroller has a sd card buildin +#undef SDCARDDETECT +#define SDCARDDETECT 49 +#undef SDCARDDETECTINVERTED +#define SDCARDDETECTINVERTED false +#undef SDSUPPORT +#define SDSUPPORT true +#endif + +class UIDisplay { + public: + volatile byte flags; // 1 = fast key action, 2 = slow key action, 4 = slow action running, 8 = key test running + byte col; // current col for buffer prefill + byte menuLevel; // current menu level, 0 = info, 1 = group, 2 = groupdata select, 3 = value change + byte menuPos[5]; // Positions in menu + void *menu[5]; // Menus active + byte menuTop[5]; // Top row in menu + int pageDelay; // Counter. If 0 page is refreshed if menuLevel is 0. + void *errorMsg; + unsigned int activeAction; // action for ok/next/previous + unsigned int lastAction; + unsigned long lastSwitch; // Last time display switched pages + unsigned long lastRefresh; + unsigned int lastButtonAction; + unsigned long lastButtonStart; + unsigned long nextRepeat; // Time of next autorepeat + unsigned int outputMask; // Output mask for backlight, leds etc. + int repeatDuration; // Time beween to actions if autorepeat is enabled + void addInt(int value,byte digits); // Print int into printCols + void addLong(long value,char digits); + void addFloat(float number, char fixdigits,byte digits); + void addStringP(PGM_P text); + void okAction(); + void nextPreviousAction(char next); + char statusMsg[17]; + char encoderPos; + int8_t encoderLast; + PGM_P statusText; + UIDisplay(); + void createChar(byte location,const byte charmap[]); + void initialize(); // Initialize display and keys + void printRow(byte r,char *txt); // Print row on display + void printRowP(byte r,PGM_P txt); + void parse(char *txt,bool ram); /// Parse output and write to printCols; + void refreshPage(); + void executeAction(int action); + void finishAction(int action); + void slowAction(); + void fastAction(); + void mediumAction(); + void pushMenu(void *men,bool refresh); + void setStatusP(PGM_P txt); + void setStatus(char *txt); + inline void setOutputMaskBits(unsigned int bits) {outputMask|=bits;} + inline void unsetOutputMaskBits(unsigned int bits) {outputMask&=~bits;} +#if SDSUPPORT + void updateSDFileCount(); + void sdrefresh(byte &r); + void goDir(char *name); + bool isDirname(char *name); + char cwd[SD_MAX_FOLDER_DEPTH*13+2]; + byte folderLevel; +#endif +}; +extern UIDisplay uid; + + +#if FEATURE_CONTROLLER==1 +#include "uiconfig.h" +#endif +#if FEATURE_CONTROLLER==0 // No controller at all +#define UI_HAS_KEYS 0 +#define UI_DISPLAY_TYPE 0 +#ifdef UI_MAIN +void ui_init_keys() {} +void ui_check_keys(int &action) {} +inline void ui_check_slow_encoder() {} +void ui_check_slow_keys(int &action) {} +#endif +#endif +#if FEATURE_CONTROLLER==2 // reprapdiscount smartcontroller +#define UI_HAS_KEYS 1 +#define UI_HAS_BACK_KEY 0 +#define UI_DISPLAY_TYPE 1 +#define UI_DISPLAY_CHARSET 1 +#define BEEPER_TYPE 1 +#define UI_COLS 20 +#define UI_ROWS 4 +#if MOTHERBOARD==80 // Rumba has different pins as RAMPS! +#define BEEPER_PIN 44 +#define UI_DISPLAY_RS_PIN 19 +#define UI_DISPLAY_RW_PIN -1 +#define UI_DISPLAY_ENABLE_PIN 42 +#define UI_DISPLAY_D0_PIN 18 +#define UI_DISPLAY_D1_PIN 38 +#define UI_DISPLAY_D2_PIN 41 +#define UI_DISPLAY_D3_PIN 40 +#define UI_DISPLAY_D4_PIN 18 +#define UI_DISPLAY_D5_PIN 38 +#define UI_DISPLAY_D6_PIN 41 +#define UI_DISPLAY_D7_PIN 40 +#define UI_ENCODER_A 12 +#define UI_ENCODER_B 11 +#define UI_ENCODER_CLICK 43 +#define UI_RESET_PIN 46 +#else +#define BEEPER_PIN 37 +#define UI_DISPLAY_RS_PIN 16 +#define UI_DISPLAY_RW_PIN -1 +#define UI_DISPLAY_ENABLE_PIN 17 +#define UI_DISPLAY_D0_PIN 23 +#define UI_DISPLAY_D1_PIN 25 +#define UI_DISPLAY_D2_PIN 27 +#define UI_DISPLAY_D3_PIN 29 +#define UI_DISPLAY_D4_PIN 23 +#define UI_DISPLAY_D5_PIN 25 +#define UI_DISPLAY_D6_PIN 27 +#define UI_DISPLAY_D7_PIN 29 +#define UI_ENCODER_A 33 +#define UI_ENCODER_B 31 +#define UI_ENCODER_CLICK 35 +#define UI_RESET_PIN 41 +#endif +#define UI_DELAYPERCHAR 320 +#define UI_INVERT_MENU_DIRECTION false +#ifdef UI_MAIN +void ui_init_keys() { + UI_KEYS_INIT_CLICKENCODER_LOW(UI_ENCODER_A,UI_ENCODER_B); // click encoder on pins 47 and 45. Phase is connected with gnd for signals. + UI_KEYS_INIT_BUTTON_LOW(UI_ENCODER_CLICK); // push button, connects gnd to pin + UI_KEYS_INIT_BUTTON_LOW(UI_RESET_PIN); // Kill pin +} +void ui_check_keys(int &action) { + UI_KEYS_CLICKENCODER_LOW_REV(UI_ENCODER_A,UI_ENCODER_B); // click encoder on pins 47 and 45. Phase is connected with gnd for signals. + UI_KEYS_BUTTON_LOW(UI_ENCODER_CLICK,UI_ACTION_OK); // push button, connects gnd to pin + UI_KEYS_BUTTON_LOW(UI_RESET_PIN,UI_ACTION_RESET); +} +inline void ui_check_slow_encoder() {} +void ui_check_slow_keys(int &action) {} +#endif +#endif // Controller 2 + +#if FEATURE_CONTROLLER==3 // Adafruit RGB controller +#define UI_HAS_KEYS 1 +#define UI_HAS_BACK_KEY 1 +#define UI_DISPLAY_TYPE 3 +#define UI_DISPLAY_CHARSET 1 +#define UI_COLS 16 +#define UI_ROWS 2 +#define UI_DISPLAY_I2C_CHIPTYPE 1 +#define UI_DISPLAY_I2C_ADDRESS 0x40 +#define UI_DISPLAY_I2C_OUTPUT_PINS 65504 +#define UI_DISPLAY_I2C_OUTPUT_START_MASK 0 +#define UI_DISPLAY_I2C_PULLUP 31 +#define UI_I2C_CLOCKSPEED 400000L +#define UI_DISPLAY_RS_PIN _BV(15) +#define UI_DISPLAY_RW_PIN _BV(14) +#define UI_DISPLAY_ENABLE_PIN _BV(13) +#define UI_DISPLAY_D0_PIN _BV(12) +#define UI_DISPLAY_D1_PIN _BV(11) +#define UI_DISPLAY_D2_PIN _BV(10) +#define UI_DISPLAY_D3_PIN _BV(9) +#define UI_DISPLAY_D4_PIN _BV(12) +#define UI_DISPLAY_D5_PIN _BV(11) +#define UI_DISPLAY_D6_PIN _BV(10) +#define UI_DISPLAY_D7_PIN _BV(9) +#define UI_INVERT_MENU_DIRECTION true +#define UI_HAS_I2C_KEYS +#define UI_HAS_I2C_ENCODER 0 +#define UI_I2C_KEY_ADDRESS 0x40 +#ifdef UI_MAIN +void ui_init_keys() {} +void ui_check_keys(int &action) {} +inline void ui_check_slow_encoder() { + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); + i2c_write(0x12); // GIOA + i2c_stop(); + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_READ); + unsigned int keymask = i2c_readAck(); + keymask = keymask + (i2c_readNak()<<8); + i2c_stop(); +} +void ui_check_slow_keys(int &action) { + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); + i2c_write(0x12); // GPIOA + i2c_stop(); + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_READ); + unsigned int keymask = i2c_readAck(); + keymask = keymask + (i2c_readNak()<<8); + i2c_stop(); + UI_KEYS_I2C_BUTTON_LOW(4,UI_ACTION_PREVIOUS); // Up button + UI_KEYS_I2C_BUTTON_LOW(8,UI_ACTION_NEXT); // down button + UI_KEYS_I2C_BUTTON_LOW(16,UI_ACTION_BACK); // left button + UI_KEYS_I2C_BUTTON_LOW(2,UI_ACTION_OK); // right button + UI_KEYS_I2C_BUTTON_LOW(1,UI_ACTION_MENU_QUICKSETTINGS); //Select button +} +#endif +#endif // Controller 3 + +#if FEATURE_CONTROLLER==4 // Foltyn 3D Master +#define UI_HAS_KEYS 1 +#define UI_HAS_BACK_KEY 1 +#define UI_DISPLAY_TYPE 1 +#define UI_DISPLAY_CHARSET 2 +#define UI_COLS 20 +#define UI_ROWS 4 +#define UI_DISPLAY_RS_PIN 63 // PINK.1, 88, D_RS +#define UI_DISPLAY_RW_PIN -1 +#define UI_DISPLAY_ENABLE_PIN 65 // PINK.3, 86, D_E +#define UI_DISPLAY_D0_PIN 59 // PINF.5, 92, D_D4 +#define UI_DISPLAY_D1_PIN 64 // PINK.2, 87, D_D5 +#define UI_DISPLAY_D2_PIN 44 // PINL.5, 40, D_D6 +#define UI_DISPLAY_D3_PIN 66 // PINK.4, 85, D_D7 +#define UI_DISPLAY_D4_PIN 59 // PINF.5, 92, D_D4 +#define UI_DISPLAY_D5_PIN 64 // PINK.2, 87, D_D5 +#define UI_DISPLAY_D6_PIN 44 // PINL.5, 40, D_D6 +#define UI_DISPLAY_D7_PIN 66 // PINK.4, 85, D_D7 +#define UI_DELAYPERCHAR 320 +#define UI_INVERT_MENU_DIRECTION false +#ifdef UI_MAIN +void ui_init_keys() { + UI_KEYS_INIT_BUTTON_LOW(4); // push button, connects gnd to pin + UI_KEYS_INIT_BUTTON_LOW(5); + UI_KEYS_INIT_BUTTON_LOW(6); + UI_KEYS_INIT_BUTTON_LOW(11); + UI_KEYS_INIT_BUTTON_LOW(42); +} +void ui_check_keys(int &action) { + UI_KEYS_BUTTON_LOW(4,UI_ACTION_OK); // push button, connects gnd to pin + UI_KEYS_BUTTON_LOW(5,UI_ACTION_NEXT); // push button, connects gnd to pin + UI_KEYS_BUTTON_LOW(6,UI_ACTION_PREVIOUS); // push button, connects gnd to pin + UI_KEYS_BUTTON_LOW(11,UI_ACTION_BACK); // push button, connects gnd to pin + UI_KEYS_BUTTON_LOW(42,UI_ACTION_SD_PRINT ); // push button, connects gnd to pin +} +inline void ui_check_slow_encoder() {} +void ui_check_slow_keys(int &action) {} +#endif +#endif // Controller 4 + + +#if FEATURE_CONTROLLER>0 + +#if UI_ROWS==4 +#if UI_COLS==16 +#define UI_LINE_OFFSETS {0,0x40,0x10,0x50} // 4x16 +#elif UI_COLS==20 +//#define UI_LINE_OFFSETS {0,0x20,0x40,0x60} // 4x20 with KS0073 +#define UI_LINE_OFFSETS {0,0x40,0x14,0x54} // 4x20 with HD44780 +#else +#error Unknown combination off rows/columns - define UI_LINE_OFFSETS manually. +#endif +#else +#define UI_LINE_OFFSETS {0,0x40,0x10,0x50} // 2x16, 2x20, 2x24 +#endif +#include "uilang.h" +#include "uimenu.h" +#endif + +#define UI_VERSION_STRING "Repetier " REPETIER_VERSION + +#ifdef UI_HAS_I2C_KEYS +#define COMPILE_I2C_DRIVER +#endif + +#if UI_DISPLAY_TYPE!=0 + + +#if UI_DISPLAY_TYPE==3 +#define COMPILE_I2C_DRIVER +#endif + +#ifndef UI_TEMP_PRECISION +#if UI_COLS>16 +#define UI_TEMP_PRECISION 1 +#else +#define UI_TEMP_PRECISION 0 +#endif +#endif + +#define UI_INITIALIZE uid.initialize(); +#define UI_FAST if(pwm_count & 4) {uid.fastAction();} +#define UI_MEDIUM uid.mediumAction(); +#define UI_SLOW uid.slowAction(); +#define UI_STATUS(status) uid.setStatusP(PSTR(status)); +#define UI_STATUS_UPD(status) {uid.setStatusP(PSTR(status));uid.refreshPage();} +#define UI_STATUS_RAM(status) uid.setStatus(status); +#define UI_STATUS_UPD_RAM(status) {uid.setStatus(status);uid.refreshPage();} +#define UI_ERROR(msg) {uid.errorMsg=PSTR(msg);pushMenu((void*)&ui_menu_error,true);} +#define UI_CLEAR_STATUS {uid.statusMsg[0]=0;} +#else +#define UI_INITIALIZE {} +#define UI_FAST {} +#define UI_MEDIUM {} +#define UI_SLOW {} +#define UI_STATUS(status) {} +#define UI_STATUS_UPD(status) {} +#define UI_CLEAR_STATUS {} +#define UI_ERROR(msg) {} +#define UI_STATUS_UPD_RAM(status) {} +#endif // Display + +// Beeper methods +#if BEEPER_TYPE==0 +#define BEEP_SHORT {} +#define BEEP_LONG {} +#else +#define BEEP_SHORT beep(BEEPER_SHORT_SEQUENCE); +#define BEEP_LONG beep(BEEPER_LONG_SEQUENCE); +#endif +extern void beep(byte duration,byte count); + +#endif + diff --git a/Repetier/uiconfig.h b/Repetier/uiconfig.h new file mode 100644 index 0000000..10a3402 --- /dev/null +++ b/Repetier/uiconfig.h @@ -0,0 +1,388 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + +*/ + +/* ===================== IMPORTANT ======================== + +The LCD and Key support is new. I tested everything as good as possible, +but some combinations may not work as supposed. +The I2C methods rely on a stable I2C connection. Noise may cause wrong signals +which can cause the firmware to freeze. + +The ui adds quite some code, so AVRs with 64kB ram (Sanguino, Gen6) can not handle all features +of the firmware at the same time. You have to disable some features to gain the +ram needed. What should work: +- No sd card - the sd card code is quite large. +- No keys attached - The longest part is the menu handling. +- EEPROM_MODE 0 and USE_OPS 0. + +Currently supported hardware: + +*** Displays *** + +- direct connected lcd with 4 data lines +- connected via i2c + +*** Keys *** + +- rotary encoder +- push button +- key matrix up to 4x4 +- rotary encoder via i2c (only slow turns are captured correct) +- push button via i2c + +*** Buzzer *** + +- directly connected, high = on +- connected via i2c, low = on + +==============================================================*/ + + +/** While the ascii chars are all the same, the driver have different charsets +for special chars used in different countries. The charset allows to fix for +this problem. If characters look wrong, try a different charset. If nothing +works, use the ascii charset 0 as fallback. Not the nicest for everything but working! + +0 = ASCII fallback +1 = Default works on most displays. This has some japanese chars in charset +2 = Alternative charset with more european chars + +*/ +#define UI_DISPLAY_CHARSET 1 + +/** Select type of beeper +0 = none +1 = Piezo connected to pin +2 = Piezo connected to a pin over I2C +*/ +#ifndef BEEPER_TYPE +#define BEEPER_TYPE 1 +#endif + +#if BEEPER_TYPE==1 && !defined(BEEPER_PIN) +#define BEEPER_PIN 79 +#endif +#if BEEPER_TYPE==2 +#define BEEPER_ADDRESS 0x40 // I2C address of the chip with the beeper pin +#define BEEPER_PIN _BV(7) // Bit value for pin 8 +#define COMPILE_I2C_DRIVER // We need the I2C driver as we are using i2c +#endif + + +/** +What display type do you use? +0 = No display +1 = LCD Display with 4 bit data bus +2 = LCD Display with 8 bit data bus (currently not implemented, fallback to 1) +3 = LCD Display with I2C connection, 4 bit mode +4 = Use the slower LiquiedCrystal library bundled with arduino. + IMPORTANT: You need to uncomment the LiquidCrystal include in Repetier.pde for it to work. + If you have Sanguino and want to use the library, you need to have Arduino 023 or older. (13.04.2012) +*/ +#define UI_DISPLAY_TYPE 1 + +/** Number of columns per row + +Typical values are 16 and 20 +*/ +#define UI_COLS 20 +/** +Rows of your display. 2 or 4 +*/ +#define UI_ROWS 4 + +/* What type of chip is used for I2C communication +0 : PCF8574 or PCF8574A or compatible chips. +1 : MCP23017 +*/ +#define UI_DISPLAY_I2C_CHIPTYPE 0 +// 0x40 till 0x4e for PCF8574, 0x40 for the adafruid RGB shield, 0x40 - 0x4e for MCP23017 +// Official addresses have a value twice as high! +#define UI_DISPLAY_I2C_ADDRESS 0x4e +// For MCP 23017 define which pins should be output +#define UI_DISPLAY_I2C_OUTPUT_PINS 65504 +// Set the output mask that is or'd over the output data. This is needed to activate +// a backlight switched over the I2C. +// The adafruit RGB shields enables a light if the bit is not set. Bits 6-8 are used for backlight. +#define UI_DISPLAY_I2C_OUTPUT_START_MASK 0 +// For MCP which inputs are with pullup. 31 = pins 0-4 for adafruid rgb shield buttons +#define UI_DISPLAY_I2C_PULLUP 31 +/* How fast should the I2C clock go. The PCF8574 work only with the lowest setting 100000. +A MCP23017 can run also with 400000 Hz */ +#define UI_I2C_CLOCKSPEED 100000L +/** +Define the pin +*/ +#if UI_DISPLAY_TYPE==3 // I2C Pin configuration +#define UI_DISPLAY_RS_PIN _BV(4) +#define UI_DISPLAY_RW_PIN _BV(5) +#define UI_DISPLAY_ENABLE_PIN _BV(6) +#define UI_DISPLAY_D0_PIN _BV(0) +#define UI_DISPLAY_D1_PIN _BV(1) +#define UI_DISPLAY_D2_PIN _BV(2) +#define UI_DISPLAY_D3_PIN _BV(3) +#define UI_DISPLAY_D4_PIN _BV(0) +#define UI_DISPLAY_D5_PIN _BV(1) +#define UI_DISPLAY_D6_PIN _BV(2) +#define UI_DISPLAY_D7_PIN _BV(3) + +// Pins for adafruid RGB shield +/*#define UI_DISPLAY_RS_PIN _BV(15) +#define UI_DISPLAY_RW_PIN _BV(14) +#define UI_DISPLAY_ENABLE_PIN _BV(13) +#define UI_DISPLAY_D0_PIN _BV(12) +#define UI_DISPLAY_D1_PIN _BV(11) +#define UI_DISPLAY_D2_PIN _BV(10) +#define UI_DISPLAY_D3_PIN _BV(9) +#define UI_DISPLAY_D4_PIN _BV(12) +#define UI_DISPLAY_D5_PIN _BV(11) +#define UI_DISPLAY_D6_PIN _BV(10) +#define UI_DISPLAY_D7_PIN _BV(9)*/ + +#else // Direct display connections +#define UI_DISPLAY_RS_PIN 70//63 // PINK.1, 88, D_RS +#define UI_DISPLAY_RW_PIN -1 +#define UI_DISPLAY_ENABLE_PIN 71// // PINK.3, 86, D_E +#define UI_DISPLAY_D0_PIN 72 // PINF.5, 92, D_D4 +#define UI_DISPLAY_D1_PIN 73 // PINK.2, 87, D_D5 +#define UI_DISPLAY_D2_PIN 74 // PINL.5, 40, D_D6 +#define UI_DISPLAY_D3_PIN 75 // PINK.4, 85, D_D7 +#define UI_DISPLAY_D4_PIN 72 // PINF.5, 92, D_D4 +#define UI_DISPLAY_D5_PIN 73 // PINK.2, 87, D_D5 +#define UI_DISPLAY_D6_PIN 74 // PINL.5, 40, D_D6 +#define UI_DISPLAY_D7_PIN 75 // PINK.4, 85, D_D7 +#define UI_DELAYPERCHAR 320 + +#endif + + +/** \brief Are some keys connected? + +0 = No keys attached - disables also menu +1 = Some keys attached +*/ +#define UI_HAS_KEYS 1 + + +/** \brief Is a back key present. + +If you have menus enabled, you need a method to leave it. If you have a back key, you can always go one level higher. +Without a back key, you need to navigate to the back entry in the menu. Setting this value to 1 removes the back entry. +*/ +#define UI_HAS_BACK_KEY 0 + +/* Then you have the next/previous keys more like up/down keys, it may be more intuitive to change the direction you skip through the menus. +If you set it to true, next will go to previous menu instead of the next menu. + +*/ +#define UI_INVERT_MENU_DIRECTION false + +/** Uncomment this, if you have keys connected via i2c to a PCF8574 chip. */ +//#define UI_HAS_I2C_KEYS + +// Do you have a I2C connected encoder? +#define UI_HAS_I2C_ENCODER 0 + +// Under which address can the key status requested. This is the address of your PCF8574 where the keys are connected. +// If you use a MCP23017 the address from display is used also for keys. +#define UI_I2C_KEY_ADDRESS 0x40 + + +#ifdef UI_MAIN +/* ####################################################################### + Key definitions + +The firmware is very flexible regarding your input methods. You can use one +or more of the predefined key macros, to define a mapper. If no matching mapper +is available, you can add you c-code for mapping directly into the keyboard +routines. The predefined macros do the same, just hiding the code behind it. + +For each key, two seperate parts must be defined. The first is the initialization +which must be added inside ui_init_keys() and the second ist a testing routine. +These come into ui_check_keys() or ui_check_slow_keys() depending on the time needed +for testing. If you are in doubt, put it in ui_check_slow_keys(). +ui_init_keys() is called from an interrupt controlling the extruder, so only +fast tests should be put there. +The detect methods need an action identifier. A list of supported ids is found +at the beginning of ui.h It's best to use the symbol name, in case the value changes. + +1. Simple push button connected to gnd if closed on a free arduino pin + init -> UI_KEYS_INIT_BUTTON_LOW(pinNumber); + detect -> UI_KEYS_BUTTON_LOW(pinNumber,action); + +2. Simple push button connected to 5v if closed on a free arduino pin + init -> UI_KEYS_INIT_BUTTON_HIGH(pinNumber); + detect -> UI_KEYS_BUTTON_HIGH(pinNumber,action); + +3. Click encoder, A/B connected to gnd if closed. + init -> UI_KEYS_INIT_CLICKENCODER_LOW(pinA,pinB); + detect -> UI_KEYS_CLICKENCODER_LOW(pinA,pinB); + or UI_KEYS_CLICKENCODER_LOW_REV(pinA,pinB); // reverse direction + If you can move the menu cursor without a click, just be adding some force in one direction, + toggle the _REV with non _REV and toggle pins. + If the direction is wrong, toggle _REV with non _REV version. + For the push button of the encoder use 1. + +4. Click encoder, A/B connected to 5V if closed. + init -> UI_KEYS_INIT_CLICKENCODER_HIGH(pinA,pinB); + detect -> UI_KEYS_CLICKENCODER_HIGH(pinA,pinB); + or UI_KEYS_CLICKENCODER_HIGH_REV(pinA,pinB); // reverse direction + If you can move the menu cursor without a click, just be adding some force in one direction, + toggle the _REV with non _REV and toggle pins. + If the direction is wrong, toggle _REV with non _REV version. + For the push button of the encoder use 2. + +5. Maxtrix keyboard with 1-4 rows and 1-4 columns. + init -> UI_KEYS_INIT_MATRIX(r1,r2,r3,r4,c1,c2,c3,c4); + detect -> UI_KEYS_MATRIX(r1,r2,r3,r4,c1,c2,c3,c4); + In addition you have to set UI_MATRIX_ACTIONS to match your desired actions. + +------- Keys connected via I2C ------------- + +All keys and the buzzer if present must be on a connected to a single PCF8574 chip! +As all I2C request take time, they belong all in ui_check_slow_keys. +Dont use the pin ids but instead _BV(pinNumber0_7) as pin id. 0 = First pin + +6. Click encoder, A/B connected to gnd if closed. + init -> not needed, but make sure UI_HAS_I2C_KEY is not commented out. + detect -> UI_KEYS_I2C_CLICKENCODER_LOW(pinA,pinB); + or UI_KEYS_I2C_CLICKENCODER_LOW_REV(pinA,pinB); // reverse direction + If you can move the menu cursor without a click, just be adding some force in one direction, + toggle the _REV with non _REV and toggle pins. + If the direction is wrong, toggle _REV with non _REV version. + For the push button of the encoder use 7. + NOTICE: The polling frequency is limited, so only slow turns are captured correct! + +7. Simple push button connected to gnd if closed via I2C on a PCF8574 + init -> not needed, but make sure UI_HAS_I2C_KEY is not commented out. + detect -> UI_KEYS_I2C_BUTTON_LOW(pinNumber,action); + +-------- Some notes on actions ------------- + +There are three kinds of actions. + +Type 1: Immediate actions - these are execute and forget actions like home/pre-heat +Type 2: Parameter change action - these change the mode for next/previous keys. They are valid + until a new change action is initiated or the action is finished with ok button. +Type 3: Show menu action. These actions have a _MENU_ in their name. If they are executed, a new + menu is pushed on the menu stack and you see the menu. If you assign these actions directly + to a key, you might not want this pushing behaviour. In this case add UI_ACTION_TOPMENU to the + action, like UI_ACTION_TOPMENU+UI_ACTION_MENU_XPOSFAST. That will show the menu as top-menu + closing all othe submenus that were open. + + ####################################################################### */ + +// Use these codes for key detect. The main menu will show the pressed action in the lcd display. +// after that assign the desired codes. +//#define UI_MATRIX_ACTIONS {2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015} +// Define your matrix actions +#define UI_MATRIX_ACTIONS {UI_ACTION_HOME_ALL, UI_ACTION_TOP_MENU, UI_ACTION_SET_ORIGIN, UI_ACTION_NEXT,\ + UI_ACTION_HOME_Z, UI_ACTION_MENU_ZPOS, UI_ACTION_COOLDOWN, UI_ACTION_OK,\ + UI_ACTION_HOME_Y, UI_ACTION_MENU_YPOSFAST, UI_ACTION_PREHEAT_ABS, UI_ACTION_PREVIOUS,\ + UI_ACTION_HOME_X, UI_ACTION_MENU_XPOSFAST, UI_ACTION_DISABLE_STEPPER, UI_ACTION_BACK} +#ifdef UI_MATRIX_ACTIONS +const int matrixActions[] PROGMEM = UI_MATRIX_ACTIONS; +#endif + +void ui_init_keys() { +#if UI_HAS_KEYS!=0 + UI_KEYS_INIT_CLICKENCODER_LOW(76,77); // click encoder on pins 47 and 45. Phase is connected with gnd for signals. + UI_KEYS_INIT_BUTTON_LOW(78); + UI_KEYS_INIT_BUTTON_LOW(KILL_PIN); + +// UI_KEYS_INIT_BUTTON_LOW(4); // push button, connects gnd to pin +// UI_KEYS_INIT_BUTTON_LOW(5); +// UI_KEYS_INIT_BUTTON_LOW(6); +// UI_KEYS_INIT_BUTTON_LOW(11); +// UI_KEYS_INIT_BUTTON_LOW(42); +// UI_KEYS_INIT_CLICKENCODER_LOW(47,45); // click encoder on pins 47 and 45. Phase is connected with gnd for signals. +// UI_KEYS_INIT_BUTTON_LOW(43); // push button, connects gnd to pin +// UI_KEYS_INIT_MATRIX(32,47,45,43,41,39,37,35); +#endif +} +void ui_check_keys(int &action) { +#if UI_HAS_KEYS!=0 + + UI_KEYS_CLICKENCODER_LOW_REV(76,77); // click encoder on pins 47 and 45. Phase is connected with gnd for signals. + UI_KEYS_BUTTON_LOW(78,UI_ACTION_OK); // push button, connects gnd to pin + UI_KEYS_BUTTON_LOW(KILL_PIN,UI_ACTION_KILL); // push button, connects gnd to pin + +// UI_KEYS_BUTTON_LOW(5,UI_ACTION_NEXT); // push button, connects gnd to pin +// UI_KEYS_BUTTON_LOW(6,UI_ACTION_PREVIOUS); // push button, connects gnd to pin +// UI_KEYS_BUTTON_LOW(11,UI_ACTION_BACK); // push button, connects gnd to pin +// UI_KEYS_BUTTON_LOW(42,UI_ACTION_SD_PRINT ); // push button, connects gnd to pin +// UI_KEYS_CLICKENCODER_LOW_REV(47,45); // click encoder on pins 47 and 45. Phase is connected with gnd for signals. +// UI_KEYS_BUTTON_LOW(43,UI_ACTION_OK); // push button, connects gnd to pin +#endif +} +inline void ui_check_slow_encoder() { +#if defined(UI_HAS_I2C_KEYS) && UI_HAS_KEYS!=0 +#if UI_DISPLAY_I2C_CHIPTYPE==0 + i2c_start_wait(UI_I2C_KEY_ADDRESS+I2C_READ); + byte keymask = i2c_readNak(); // Read current key mask +#endif +#if UI_DISPLAY_I2C_CHIPTYPE==1 + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); + i2c_write(0x12); // GIOA + i2c_stop(); + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_READ); + unsigned int keymask = i2c_readAck(); + keymask = keymask + (i2c_readNak()<<8); +#endif + i2c_stop(); + // Add I2C click encoder tests here, all other i2c tests and a copy of the encoder test belog in ui_check_slow_keys + UI_KEYS_I2C_CLICKENCODER_LOW_REV(_BV(2),_BV(0)); // click encoder on pins 0 and 2. Phase is connected with gnd for signals. +#endif +} +void ui_check_slow_keys(int &action) { +#if defined(UI_HAS_I2C_KEYS) && UI_HAS_KEYS!=0 +#if UI_DISPLAY_I2C_CHIPTYPE==0 + i2c_start_wait(UI_I2C_KEY_ADDRESS+I2C_READ); + byte keymask = i2c_readNak(); // Read current key mask +#endif +#if UI_DISPLAY_I2C_CHIPTYPE==1 + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); + i2c_write(0x12); // GPIOA + i2c_stop(); + i2c_start_wait(UI_DISPLAY_I2C_ADDRESS+I2C_READ); + unsigned int keymask = i2c_readAck(); + keymask = keymask + (i2c_readNak()<<8); +#endif + i2c_stop(); + // Add I2C key tests here + UI_KEYS_I2C_CLICKENCODER_LOW_REV(_BV(2),_BV(0)); // click encoder on pins 0 and 2. Phase is connected with gnd for signals. + UI_KEYS_I2C_BUTTON_LOW(_BV(1),UI_ACTION_OK); // push button, connects gnd to pin + UI_KEYS_I2C_BUTTON_LOW(_BV(3),UI_ACTION_BACK); // push button, connects gnd to pin + UI_KEYS_I2C_BUTTON_LOW(_BV(4),UI_ACTION_MENU_QUICKSETTINGS+UI_ACTION_TOPMENU); // push button, connects gnd to pin + UI_KEYS_I2C_BUTTON_LOW(_BV(5),UI_ACTION_MENU_EXTRUDER+UI_ACTION_TOPMENU); // push button, connects gnd to pin + UI_KEYS_I2C_BUTTON_LOW(_BV(6),UI_ACTION_MENU_POSITIONS+UI_ACTION_TOPMENU); // push button, connects gnd to pin +/* + // Button handling for the Adafruit RGB shild + UI_KEYS_I2C_BUTTON_LOW(4,UI_ACTION_PREVIOUS); // Up button + UI_KEYS_I2C_BUTTON_LOW(8,UI_ACTION_NEXT); // down button + UI_KEYS_I2C_BUTTON_LOW(16,UI_ACTION_BACK); // left button + UI_KEYS_I2C_BUTTON_LOW(2,UI_ACTION_OK); // right button + UI_KEYS_I2C_BUTTON_LOW(1,UI_ACTION_MENU_QUICKSETTINGS); //Select button + // ----- End RGB shield ---------- + */ +#endif + + //UI_KEYS_MATRIX(32,47,45,43,41,39,37,35); +} + +#endif diff --git a/Repetier/uilang.h b/Repetier/uilang.h new file mode 100644 index 0000000..9987ab6 --- /dev/null +++ b/Repetier/uilang.h @@ -0,0 +1,671 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + +*/ + +#if !defined(UI_DISPLAY_CHARSET) || UI_DISPLAY_CHARSET>2 +#define UI_DISPLAY_CHARSET 1 +#endif + +#if UI_DISPLAY_CHARSET==0 // ASCII fallback +#define CHAR_RIGHT '-' +#define CHAR_SELECTOR '>' +#define CHAR_SELECTED '*' +#define STR_auml "ae" +#define STR_Auml "Ae" +#define STR_uuml "ue" +#define STR_Uuml "Ue" +#define STR_ouml "oe" +#define STR_Ouml "Oe" +#define STR_szlig "ss" +#endif + +#if UI_DISPLAY_CHARSET==1 // HD44870 charset with knji chars +#define CHAR_RIGHT 0x7e +#define CHAR_SELECTOR '>' +#define CHAR_SELECTED '*' +#define STR_auml "\xe1" +#define STR_Auml "Ae" +#define STR_uuml "\365" +#define STR_Uuml "Ue" +#define STR_ouml "\357" +#define STR_Ouml "Oe" +#define STR_szlig "\342" +#endif + +#if UI_DISPLAY_CHARSET==2 // Western charset +#define CHAR_RIGHT 0xbc +#define CHAR_SELECTOR 0xf6 +#define CHAR_SELECTED '*' +#define STR_auml "\204" +#define STR_Auml "\216" +#define STR_uuml "\201" +#define STR_Uuml "\212" +#define STR_ouml "\204" +#define STR_Ouml "\211" +#define STR_szlig "160" +#endif + + + +// At first all terms in english are defined. After that the selected language +// can overwrite the definition. That way new strings are at least in english +// available. + +#define UI_TEXT_ON "On" +#define UI_TEXT_OFF "Off" +#define UI_TEXT_NA "N/A" // Output for not available +#define UI_TEXT_YES "Yes" +#define UI_TEXT_NO "No" +#define UI_TEXT_SEL "\003" +#define UI_TEXT_NOSEL "\004" +#define UI_TEXT_PRINT_POS "Printing..." +#define UI_TEXT_PRINTING "Printing" +#define UI_TEXT_IDLE "Idle" +#define UI_TEXT_NOSDCARD "No SD Card" +#define UI_TEXT_ERROR "**** ERROR ****" +#define UI_TEXT_BACK "Back \001" +#define UI_TEXT_QUICK_SETTINGS "Quick Settings" +#define UI_TEXT_CONFIGURATION "Configuration" +#define UI_TEXT_POSITION "Position" +#define UI_TEXT_EXTRUDER "Extruder" +#define UI_TEXT_SD_CARD "SD Card" +#define UI_TEXT_DEBUGGING "Debugging" +#define UI_TEXT_HOME_DELTA "Home Delta" +#define UI_TEXT_HOME_ALL "Home All" +#define UI_TEXT_HOME_X "Home X" +#define UI_TEXT_HOME_Y "Home Y" +#define UI_TEXT_HOME_Z "Home Z" +#define UI_TEXT_PREHEAT_PLA "Preheat PLA" +#define UI_TEXT_PREHEAT_ABS "Preheat ABS" +#define UI_TEXT_COOLDOWN "Cooldown" +#define UI_TEXT_SET_TO_ORIGIN "Set to Origin" +#define UI_TEXT_DISABLE_STEPPER "Disable stepper" +#define UI_TEXT_X_POSITION "X Position" +#define UI_TEXT_X_POS_FAST "X Pos. Fast" +#define UI_TEXT_Y_POSITION "Y Position" +#define UI_TEXT_Y_POS_FAST "Y Pos. Fast" +#define UI_TEXT_Z_POSITION "Z Position" +#define UI_TEXT_Z_POS_FAST "Z Pos. Fast" +#define UI_TEXT_E_POSITION "Extr. position" +#define UI_TEXT_BED_TEMP "Bed Temp.:%Eb\002C" +#define UI_TEXT_EXTR0_TEMP "Temp. 0 :%E0\002C" +#define UI_TEXT_EXTR1_TEMP "Temp. 1 :%E0\002C" +#define UI_TEXT_EXTR0_OFF "Extruder 0 Off" +#define UI_TEXT_EXTR1_OFF "Extruder 1 Off" +#define UI_TEXT_EXTR0_SELECT "%X0 Select Extr.0" +#define UI_TEXT_EXTR1_SELECT "%X1 Select Extr.1" +#define UI_TEXT_EXTR_ORIGIN "Set Origin" +#define UI_TEXT_PRINT_X "Print X:%ax" +#define UI_TEXT_PRINT_Y "Print Y:%ay" +#define UI_TEXT_PRINT_Z "Print Z:%az" +#define UI_TEXT_MOVE_X "Move X :%aX" +#define UI_TEXT_MOVE_Y "Move Y :%aY" +#define UI_TEXT_MOVE_Z "Move Z :%aZ" +#define UI_TEXT_JERK "Jerk :%aj" +#define UI_TEXT_ZJERK "Z-Jerk :%aJ" +#define UI_TEXT_ACCELERATION "Acceleration" +#define UI_TEXT_STORE_TO_EEPROM "Store to EEPROM" +#define UI_TEXT_LOAD_EEPROM "Load f. EEPROM" +#define UI_TEXT_DBG_ECHO "Echo :%do" +#define UI_TEXT_DBG_INFO "Info :%di" +#define UI_TEXT_DBG_ERROR "Errors :%de" +#define UI_TEXT_DBG_DRYRUN "Dry run:%dd" +#define UI_TEXT_OPS_OFF "%O0 OPS Off" +#define UI_TEXT_OPS_CLASSIC "%O1 OPS Classic" +#define UI_TEXT_OPS_FAST "%O2 OPS Fast" +#define UI_TEXT_OPS_RETRACT "Retract :%Or" +#define UI_TEXT_OPS_BACKSLASH "Backsl. :%Ob" +#define UI_TEXT_OPS_MINDIST "Min.dist :%Od" +#define UI_TEXT_OPS_MOVE_AFTER "Move after:%Oa" +#define UI_TEXT_ANTI_OOZE "Anti Ooze" +#define UI_TEXT_PRINT_FILE "Print file" +#define UI_TEXT_PAUSE_PRINT "Pause Print" +#define UI_TEXT_CONTINUE_PRINT "Continue Print" +#define UI_TEXT_UNMOUNT_CARD "Unmount Card" +#define UI_TEXT_MOUNT_CARD "Mount Card" +#define UI_TEXT_DELETE_FILE "Delete file" +#define UI_TEXT_FEEDRATE "Feedrate" +#define UI_TEXT_FEED_MAX_X "Max X:%fx" +#define UI_TEXT_FEED_MAX_Y "Max Y:%fy" +#define UI_TEXT_FEED_MAX_Z "Max Z:%fz" +#define UI_TEXT_FEED_HOME_X "Home X:%fX" +#define UI_TEXT_FEED_HOME_Y "Home Y:%fY" +#define UI_TEXT_FEED_HOME_Z "Home Z:%fZ" +#define UI_TEXT_ACTION_XPOSITION4 "X:%x0 mm","Min endstop:%sx","Max endstop:%sX","" +#define UI_TEXT_ACTION_YPOSITION4 "Y:%x1 mm","Min endstop:%sy","Max endstop:%sY","" +#define UI_TEXT_ACTION_ZPOSITION4 "Z:%x2 mm","Min endstop:%sz","Max endstop:%sZ","" +#define UI_TEXT_ACTION_XPOSITION_FAST4 "X:%x0 mm","Min endstop:%sx","Max endstop:%sX","" +#define UI_TEXT_ACTION_YPOSITION_FAST4 "Y:%x1 mm","Min endstop:%sy","Max endstop:%sY","" +#define UI_TEXT_ACTION_ZPOSITION_FAST4 "Z:%x2 mm","Min endstop:%sz","Max endstop:%sZ","" +#define UI_TEXT_ACTION_EPOSITION_FAST2 "E:%x3 mm","1 click = 1 mm" +#define UI_TEXT_ACTION_XPOSITION2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_ACTION_XPOSITION_FAST2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION_FAST2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION_FAST2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_FANSPEED "Fan speed" +#define UI_TEXT_FAN_OFF "Turn Fan Off" +#define UI_TEXT_FAN_25 "Set Fan 25%%%" +#define UI_TEXT_FAN_50 "Set Fan 50%%%" +#define UI_TEXT_FAN_75 "Set Fan 75%%%" +#define UI_TEXT_FAN_FULL "Set Fan Full" +#define UI_TEXT_STEPPER_INACTIVE "Stepper Inactive" +#define UI_TEXT_STEPPER_INACTIVE2 "Dis. After:%is","[s] 0=Off" +#define UI_TEXT_POWER_INACTIVE "Max. Inactive" +#define UI_TEXT_POWER_INACTIVE2 "Dis. After:%ip","[s] 0=Off" +#define UI_TEXT_GENERAL "General" +#define UI_TEXT_BAUDRATE "Baudrate:%oc" +#define UI_TEXT_EXTR_STEPS "Steps/MM:%Se" +#define UI_TEXT_EXTR_START_FEED "Start FR:%Xf" +#define UI_TEXT_EXTR_MAX_FEED "Max FR:%XF" +#define UI_TEXT_EXTR_ACCEL "Accel:%XA" +#define UI_TEXT_EXTR_WATCH "Stab.Time:%Xw" +#define UI_TEXT_EXTR_ADVANCE_L "Advance lin:%Xl" +#define UI_TEXT_EXTR_ADVANCE_K "Advance quad:%Xa" +#define UI_TEXT_EXTR_MANAGER "Control:%Xh" +#define UI_TEXT_EXTR_PGAIN "PID P:%Xp" +#define UI_TEXT_EXTR_IGAIN "PID I:%Xi" +#define UI_TEXT_EXTR_DGAIN "PID D:%Xd" +#define UI_TEXT_EXTR_DMIN "Drive Min:%Xm" +#define UI_TEXT_EXTR_DMAX "Drive Max:%XM" +#define UI_TEXT_EXTR_PMAX "PID Max:%XD" +#define UI_TEXT_EXTR_XOFF "X-Offset:%Xx" +#define UI_TEXT_EXTR_YOFF "Y-Offset:%Xy" +#define UI_TEXT_STRING_HM_BANGBANG "BangBang" +#define UI_TEXT_STRING_HM_PID "PID" +#define UI_TEXT_STRING_ACTION "Action:%la" +#define UI_TEXT_HEATING_EXTRUDER "Heating Extruder" +#define UI_TEXT_HEATING_BED "Heating Bed" +#define UI_TEXT_KILLED "Killed" +#define UI_TEXT_STEPPER_DISABLED "Stepper Disabled" +#define UI_TEXT_EEPROM_STORED "Configuration","stored in EEPROM" +#define UI_TEXT_EEPROM_LOADED "Configuration","loaded f. EEPROM" +#define UI_TEXT_UPLOADING "Uploading..." +#define UI_TEXT_PAGE_BUFFER "Buffer:%oB" +#define UI_TEXT_PAGE_EXTRUDER "E:%ec/%Ec\002C\176%oC" +#define UI_TEXT_PAGE_EXTRUDER1 "E1:%e0/%E0\002C\176%o0" +#define UI_TEXT_PAGE_EXTRUDER2 "E2:%e1/%E1\002C\176%o1" +#define UI_TEXT_PAGE_BED "B:%eb/%Eb\002C\176%ob" +#define UI_TEXT_SPEED_MULTIPLY "Speed Mul.:%om%%%" +#define UI_TEXT_FLOW_MULTIPLY "Flow Mul. :%of%%%" +#define UI_TEXT_SHOW_MEASUREMENT "Show meas." +#define UI_TEXT_RESET_MEASUREMENT "Reset meas." +#define UI_TEXT_SET_MEASURED_ORIGIN "Set meas. origin" +#define UI_TEXT_DELTA "Delta Calib." +#define UI_TEXT_SET_P1 "Set P1" +#define UI_TEXT_SET_P2 "Set P2" +#define UI_TEXT_SET_P3 "Set P3" +#define UI_TEXT_CALCULATE_LEVELING "Calculate Leveling" +#define UI_TEXT_LEVEL "Level delta" +#define UI_TEXT_EXTR_WAIT_RETRACT_TEMP "Wait Temp. %XT\002C" +#define UI_TEXT_EXTR_WAIT_RETRACT_UNITS "Wait Units: %XU mm" +#define UI_TEXT_SD_REMOVED "SD Card removed" +#define UI_TEXT_SD_INSERTED "SD Card inserted" +#define UI_TEXT_PRINTER_READY "Printer ready." + +// *************** german translation **************** + +#if UI_LANGUAGE==1 + +#define UI_TEXT_ON "An" +#define UI_TEXT_OFF "Aus" +#define UI_TEXT_NA "nv" +#define UI_TEXT_YES "Ja" +#define UI_TEXT_NO "Nein" +#define UI_TEXT_PRINT_POS "Drucke..." +#define UI_TEXT_PRINTING "Drucken" +#define UI_TEXT_IDLE "Leerlauf" +#define UI_TEXT_NOSDCARD "Keine SD Karte" +#define UI_TEXT_ERROR "**** FEHLER ****" +#define UI_TEXT_BACK "Zur" STR_uuml "ck \001" +#define UI_TEXT_QUICK_SETTINGS "Schnelleinst." +#define UI_TEXT_CONFIGURATION "Konfiguration" +#define UI_TEXT_POSITION "Position" +#define UI_TEXT_EXTRUDER "Extruder" +#define UI_TEXT_SD_CARD "SD Karte" +#define UI_TEXT_DEBUGGING "Debugging" +#define UI_TEXT_HOME_ALL "Home Alle" +#define UI_TEXT_HOME_X "Home X" +#define UI_TEXT_HOME_Y "Home Y" +#define UI_TEXT_HOME_Z "Home Z" +#define UI_TEXT_PREHEAT_PLA "Vorheizen PLA" +#define UI_TEXT_PREHEAT_ABS "Vorheizen ABS" +#define UI_TEXT_COOLDOWN "Abk" STR_uuml "hlen" +#define UI_TEXT_SET_TO_ORIGIN "Setze Nullpunkt" +#define UI_TEXT_DISABLE_STEPPER "Motoren Aussch." +#define UI_TEXT_X_POSITION "X Position" +#define UI_TEXT_X_POS_FAST "X Pos. Schnell" +#define UI_TEXT_Y_POSITION "Y Position" +#define UI_TEXT_Y_POS_FAST "Y Pos. Schnell" +#define UI_TEXT_Z_POSITION "Z Position" +#define UI_TEXT_Z_POS_FAST "Z Pos. Schnell" +#define UI_TEXT_E_POSITION "Extr. Position" +#define UI_TEXT_BED_TEMP "Bed Temp.:%Eb\002C" +#define UI_TEXT_EXTR0_TEMP "Temp. 0 :%E0\002C" +#define UI_TEXT_EXTR1_TEMP "Temp. 1 :%E0\002C" +#define UI_TEXT_EXTR0_OFF "Extruder 0 Aus" +#define UI_TEXT_EXTR1_OFF "Extruder 1 Aus" +#define UI_TEXT_EXTR0_SELECT "W" STR_auml "hle Extr. 0" +#define UI_TEXT_EXTR1_SELECT "W" STR_auml "hle Extr. 1" +#define UI_TEXT_EXTR_ORIGIN "Setze Nullpunkt" +#define UI_TEXT_PRINT_X "Drucken X:%ax" +#define UI_TEXT_PRINT_Y "Drucken Y:%ay" +#define UI_TEXT_PRINT_Z "Drucken Z:%az" +#define UI_TEXT_MOVE_X "Bewegen X:%aX" +#define UI_TEXT_MOVE_Y "Bewegen Y:%aY" +#define UI_TEXT_MOVE_Z "Bewegen Z:%aZ" +#define UI_TEXT_JERK "Ruck :%aj" +#define UI_TEXT_ZJERK "Z-Ruck :%aJ" +#define UI_TEXT_ACCELERATION "Beschleunigung" +#define UI_TEXT_STORE_TO_EEPROM "Sichere EEPROM" +#define UI_TEXT_LOAD_EEPROM "Lade vom EEPROM" +#define UI_TEXT_DBG_ECHO "Echo :%do" +#define UI_TEXT_DBG_INFO "Info :%di" +#define UI_TEXT_DBG_ERROR "Fehler :%de" +#define UI_TEXT_DBG_DRYRUN "Trockenlauf:%dd" +#define UI_TEXT_OPS_OFF "%O0 OPS Aus" +#define UI_TEXT_OPS_CLASSIC "%O1 OPS Klassisch" +#define UI_TEXT_OPS_FAST "%O2 OPS Schnell" +#define UI_TEXT_OPS_RETRACT "Einfahren :%Or" +#define UI_TEXT_OPS_BACKSLASH "Backsl. :%Ob" +#define UI_TEXT_OPS_MINDIST "Min.Abst. :%Od" +#define UI_TEXT_OPS_MOVE_AFTER "Start nach:%Oa" +#define UI_TEXT_ANTI_OOZE "Anti Ooze" +#define UI_TEXT_PRINT_FILE "Drucke Datei" +#define UI_TEXT_PAUSE_PRINT "Druck Pausieren" +#define UI_TEXT_CONTINUE_PRINT "Druck Forts." +#define UI_TEXT_UNMOUNT_CARD "Unmount Karte" +#define UI_TEXT_MOUNT_CARD "Mount Karte" +#define UI_TEXT_DELETE_FILE "Datei l" STR_ouml "schen" +#define UI_TEXT_FEED_MAX_X "Max X:%fx" +#define UI_TEXT_FEED_MAX_Y "Max Y:%fy" +#define UI_TEXT_FEED_MAX_Z "Max Z:%fz" +#define UI_TEXT_FEED_HOME_X "Home X:%fX" +#define UI_TEXT_FEED_HOME_Y "Home Y:%fY" +#define UI_TEXT_FEED_HOME_Z "Home Z:%fZ" +#define UI_TEXT_ACTION_XPOSITION4 "X:%x0 mm","Min Endstopp:%sx","Max Endstopp:%sX","" +#define UI_TEXT_ACTION_YPOSITION4 "Y:%x1 mm","Min Endstopp:%sy","Max Endstopp:%sY","" +#define UI_TEXT_ACTION_ZPOSITION4 "Z:%x2 mm","Min Endstopp:%sz","Max Endstopp:%sZ","" +#define UI_TEXT_ACTION_XPOSITION_FAST4 "X:%x0 mm","Min Endstopp:%sx","Max Endstopp:%sX","" +#define UI_TEXT_ACTION_YPOSITION_FAST4 "Y:%x1 mm","Min Endstopp:%sy","Max Endstopp:%sY","" +#define UI_TEXT_ACTION_ZPOSITION_FAST4 "Z:%x2 mm","Min Endstopp:%sz","Max Endstopp:%sZ","" +#define UI_TEXT_ACTION_EPOSITION_FAST2 "E:%x3 mm","1 klick = 1 mm" +#define UI_TEXT_ACTION_XPOSITION2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_ACTION_XPOSITION_FAST2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION_FAST2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION_FAST2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_FANSPEED "L" STR_uuml "fter" +#define UI_TEXT_FAN_OFF "L" STR_uuml "fter Aus" +#define UI_TEXT_FAN_25 "L" STR_uuml "fter auf 25%%%" +#define UI_TEXT_FAN_50 "L" STR_uuml "fter auf 50%%%" +#define UI_TEXT_FAN_75 "L" STR_uuml "fter auf 75%%%" +#define UI_TEXT_FAN_FULL "L" STR_uuml "fter Voll" +#define UI_TEXT_STEPPER_INACTIVE "Motor Inaktiv" +#define UI_TEXT_STEPPER_INACTIVE2 "Aus nach:%is","[s] 0=Aus" +#define UI_TEXT_POWER_INACTIVE "Max. Inaktiv" +#define UI_TEXT_POWER_INACTIVE2 "Aus nach:%ip","[s] 0=Aus" +#define UI_TEXT_GENERAL "Allgemein" +#define UI_TEXT_BAUDRATE "Baudrate:%oc" +#define UI_TEXT_EXTR_STEPS "Schr/MM:%Se" +#define UI_TEXT_EXTR_START_FEED "Start FR:%Xf" +#define UI_TEXT_EXTR_MAX_FEED "Max FR:%XF" +#define UI_TEXT_EXTR_ACCEL "Beschl.:%XA" +#define UI_TEXT_EXTR_WATCH "Stab.Zeit:%Xw" +#define UI_TEXT_EXTR_ADVANCE_L "Advance lin:Xl%" +#define UI_TEXT_EXTR_ADVANCE_K "Advance quad:Xa%" +#define UI_TEXT_EXTR_MANAGER "Regler:%Xh" +#define UI_TEXT_EXTR_PGAIN "PID P:%Xp" +#define UI_TEXT_EXTR_IGAIN "PID I:%Xi" +#define UI_TEXT_EXTR_DGAIN "PID D:%Xd" +#define UI_TEXT_EXTR_DMIN "Drive Min:%Xm" +#define UI_TEXT_EXTR_DMAX "Drive Max:%XM" +#define UI_TEXT_EXTR_PMAX "PID Max:%XD" +#define UI_TEXT_EXTR_XOFF "X-Offset:%Xx" +#define UI_TEXT_EXTR_YOFF "Y-Offset:%Xy" +#define UI_TEXT_STRING_HM_BANGBANG "BangBang" +#define UI_TEXT_STRING_HM_PID "PID" +#define UI_TEXT_STRING_ACTION "Aktion:%la" +#define UI_TEXT_HEATING_EXTRUDER "Heize Extruder" +#define UI_TEXT_HEATING_BED "Heize Druckbett" +#define UI_TEXT_KILLED "Angehalten" +#define UI_TEXT_STEPPER_DISABLED "Motoren Aus" +#define UI_TEXT_EEPROM_STORED "Konfiguration","gespeichert." +#define UI_TEXT_EEPROM_LOADED "Konfiguration","geladen." +#define UI_TEXT_UPLOADING "Hochladen..." +#define UI_TEXT_PAGE_BUFFER "Puffer:%oB" +#define UI_TEXT_PAGE_EXTRUDER "E:%ec/%Ec\002C\176%oC" +#define UI_TEXT_PAGE_EXTRUDER1 "E1:%e0/%E0\002C\176%o0" +#define UI_TEXT_PAGE_EXTRUDER2 "E2:%e1/%E1\002C\176%o1" +#define UI_TEXT_PAGE_BED "B:%eb/%Eb\002C\176%ob" +#define UI_TEXT_SPEED_MULTIPLY "Geschw.Mul:%om%%%" +#define UI_TEXT_FLOW_MULTIPLY "Flow Mul.:%of%%%" +#define UI_TEXT_ADVANCE_L "Advance lin:Xl%" +#define UI_TEXT_ADVANCE_K "Advance quad:Xa%" +#define UI_TEXT_SHOW_MEASUREMENT "Zeige Messung" +#define UI_TEXT_RESET_MEASUREMENT "Reset Messung" +#define UI_TEXT_SET_MEASURED_ORIGIN "Setze Ursprung Mess." +#define UI_TEXT_DELTA "Delta Calib." +#define UI_TEXT_SET_P1 "Setze P1" +#define UI_TEXT_SET_P2 "Setze P2" +#define UI_TEXT_SET_P3 "Setze P3" +#define UI_TEXT_CALCULATE_LEVELING "Berechne Leveling" +#define UI_TEXT_LEVEL "Level delta" +#define UI_TEXT_EXTR_WAIT_RETRACT_TEMP "Wait Temp.%XT\002C" +#define UI_TEXT_EXTR_WAIT_RETRACT_UNITS "Wait Units:%XUmm" +#define UI_TEXT_SD_REMOVED "Karte entfernt" +#define UI_TEXT_SD_INSERTED "Karte eingelegt" +#define UI_TEXT_PRINTER_READY "Drucker bereit." + +#endif + +// Dutch translation +#if UI_LANGUAGE==2 + +#define UI_TEXT_ON "Aan" +#define UI_TEXT_OFF "Uit" +#define UI_TEXT_NA "N/A" // Output for not available +#define UI_TEXT_YES "Ja" +#define UI_TEXT_NO "Nee" +#define UI_TEXT_SEL "\003" +#define UI_TEXT_NOSEL "\004" +#define UI_TEXT_PRINT_POS "Printen..." +#define UI_TEXT_PRINTING "Printen" +#define UI_TEXT_IDLE "Rust" +#define UI_TEXT_NOSDCARD "Geen SD Kaart" +#define UI_TEXT_ERROR "**** FOUT ****" +#define UI_TEXT_BACK "Terug \001" +#define UI_TEXT_QUICK_SETTINGS "Snel Instelling" +#define UI_TEXT_CONFIGURATION "Configuratie" +#define UI_TEXT_POSITION "Positie" +#define UI_TEXT_EXTRUDER "Extruder" +#define UI_TEXT_SD_CARD "SD Kaart" +#define UI_TEXT_DEBUGGING "Debugging" +#define UI_TEXT_HOME_ALL "Home Alles" +#define UI_TEXT_HOME_X "Home X" +#define UI_TEXT_HOME_Y "Home Y" +#define UI_TEXT_HOME_Z "Home Z" +#define UI_TEXT_PREHEAT "Voorverwarmen" +#define UI_TEXT_COOLDOWN "Koelen" +#define UI_TEXT_SET_TO_ORIGIN "Zet oorsprong" +#define UI_TEXT_DISABLE_STEPPER "Zet motor uit" +#define UI_TEXT_X_POSITION "X Positie" +#define UI_TEXT_X_POS_FAST "X Pos. Snel" +#define UI_TEXT_Y_POSITION "Y Positie" +#define UI_TEXT_Y_POS_FAST "Y Pos. Snel" +#define UI_TEXT_Z_POSITION "Z Positie" +#define UI_TEXT_Z_POS_FAST "Z Pos. Snel" +#define UI_TEXT_E_POSITION "Extr. positie" +#define UI_TEXT_BED_TEMP "Bed Temp.:%Eb\002C" +#define UI_TEXT_EXTR0_TEMP "Temp. 0 :%E0\002C" +#define UI_TEXT_EXTR1_TEMP "Temp. 1 :%E0\002C" +#define UI_TEXT_EXTR0_OFF "Extruder 0 Uit" +#define UI_TEXT_EXTR1_OFF "Extruder 1 Uit" +#define UI_TEXT_EXTR0_SELECT "%X0 Select Extr.0" +#define UI_TEXT_EXTR1_SELECT "%X1 Select Extr.1" +#define UI_TEXT_EXTR_ORIGIN "Zet oorsprong" +#define UI_TEXT_PRINT_X "Print X:%ax" +#define UI_TEXT_PRINT_Y "Print Y:%ay" +#define UI_TEXT_PRINT_Z "Print Z:%az" +#define UI_TEXT_MOVE_X "Beweeg X:%aX" +#define UI_TEXT_MOVE_Y "Beweeg Y:%aY" +#define UI_TEXT_MOVE_Z "Beweeg Z:%aZ" +#define UI_TEXT_JERK "Ruk:%aj" +#define UI_TEXT_ZJERK "Z-Ruk:%aJ" +#define UI_TEXT_ACCELERATION "Acceleratie" +#define UI_TEXT_STORE_TO_EEPROM "Opslaan n. EEPROM" +#define UI_TEXT_LOAD_EEPROM "Ladd f. EEPROM" +#define UI_TEXT_DBG_ECHO "Echo :%do" +#define UI_TEXT_DBG_INFO "Info :%di" +#define UI_TEXT_DBG_ERROR "Fouten :%de" +#define UI_TEXT_DBG_DRYRUN "Droogloop:%dd" +#define UI_TEXT_OPS_OFF "%O0 OPS Uit" +#define UI_TEXT_OPS_CLASSIC "%O1 OPS Klassiek" +#define UI_TEXT_OPS_FAST "%O2 OPS Snel" +#define UI_TEXT_OPS_RETRACT "Terugtrekken:%Or" +#define UI_TEXT_OPS_BACKSLASH "Backsl. :%Ob" +#define UI_TEXT_OPS_MINDIST "Min. afstand:%Od" +#define UI_TEXT_OPS_MOVE_AFTER "Beweeg na:%Oa" +#define UI_TEXT_ANTI_OOZE "Anti lekken" +#define UI_TEXT_PRINT_FILE "Print bestand" +#define UI_TEXT_PAUSE_PRINT "Pauzeer Print" +#define UI_TEXT_CONTINUE_PRINT "Zet print voort" +#define UI_TEXT_UNMOUNT_CARD "Ontkoppel Kaart" +#define UI_TEXT_MOUNT_CARD "Koppel Kaart" +#define UI_TEXT_DELETE_FILE "Verw. bestand" +#define UI_TEXT_FEEDRATE "Beweeg snelheid" +#define UI_TEXT_FEED_MAX_X "Max X:%fx" +#define UI_TEXT_FEED_MAX_Y "Max Y:%fy" +#define UI_TEXT_FEED_MAX_Z "Max Z:%fz" +#define UI_TEXT_FEED_HOME_X "Home X:%fX" +#define UI_TEXT_FEED_HOME_Y "Home Y:%fY" +#define UI_TEXT_FEED_HOME_Z "Home Z:%fZ" +#define UI_TEXT_ACTION_XPOSITION4 "X:%x0 mm","Min eindst.:%sx","Max eindst.:%sX","" +#define UI_TEXT_ACTION_YPOSITION4 "Y:%x1 mm","Min eindst.:%sy","Max eindst.:%sY","" +#define UI_TEXT_ACTION_ZPOSITION4 "Z:%x2 mm","Min eindst.:%sz","Max eindst.:%sZ","" +#define UI_TEXT_ACTION_XPOSITION_FAST4 "X:%x0 mm","Min eindst.:%sx","Max eindst.:%sX","" +#define UI_TEXT_ACTION_YPOSITION_FAST4 "Y:%x1 mm","Min eindst.:%sy","Max eindst.:%sY","" +#define UI_TEXT_ACTION_ZPOSITION_FAST4 "Z:%x2 mm","Min eindst.:%sz","Max eindst.:%sZ","" +#define UI_TEXT_ACTION_EPOSITION_FAST2 "E:%x3 mm","1 klik = 1 mm" +#define UI_TEXT_ACTION_XPOSITION2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_ACTION_XPOSITION_FAST2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION_FAST2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION_FAST2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_FANSPEED "Fan snelheid" +#define UI_TEXT_FAN_OFF "Zet Fan uit" +#define UI_TEXT_FAN_25 "Zet Fan 25%%%" +#define UI_TEXT_FAN_50 "Zet Fan 50%%%" +#define UI_TEXT_FAN_75 "Zet Fan 75%%%" +#define UI_TEXT_FAN_FULL "Zet Fan Vol aan" +#define UI_TEXT_STEPPER_INACTIVE "Motor Inactief" +#define UI_TEXT_STEPPER_INACTIVE2 "Uit na:%is","[s] 0=Off" +#define UI_TEXT_POWER_INACTIVE "Max. Inactief" +#define UI_TEXT_POWER_INACTIVE2 "Zet uit na:%ip","[s] 0=Off" +#define UI_TEXT_GENERAL "Algemeen" +#define UI_TEXT_BAUDRATE "Baudrate:%oc" +#define UI_TEXT_EXTR_STEPS "Stappen/MM:%Se" +#define UI_TEXT_EXTR_START_FEED "Start FR.:%Xf" +#define UI_TEXT_EXTR_MAX_FEED "Max FR.:%XF" +#define UI_TEXT_EXTR_ACCEL "Accel:%XA" +#define UI_TEXT_EXTR_WATCH "Stab.Tijd:%Xw" +#define UI_TEXT_EXTR_ADVANCE_L "Advance lin:Xl%" +#define UI_TEXT_EXTR_ADVANCE_K "Advance quad:Xa%" +#define UI_TEXT_EXTR_MANAGER "Control:%Xh" +#define UI_TEXT_EXTR_PGAIN "PID P:%Xp" +#define UI_TEXT_EXTR_IGAIN "PID I:%Xi" +#define UI_TEXT_EXTR_DGAIN "PID D:%Xd" +#define UI_TEXT_EXTR_DMIN "Drive Min:%Xm" +#define UI_TEXT_EXTR_DMAX "Drive Max:%XM" +#define UI_TEXT_EXTR_PMAX "PID Max:%XD" +#define UI_TEXT_EXTR_XOFF "X-Offset:%Xx" +#define UI_TEXT_EXTR_YOFF "Y-Offset:%Xy" +#define UI_TEXT_STRING_HM_BANGBANG "BangBang" +#define UI_TEXT_STRING_HM_PID "PID" +#define UI_TEXT_STRING_ACTION "Action:%la" +#define UI_TEXT_HEATING_EXTRUDER "Opwarmen Extru." +#define UI_TEXT_HEATING_BED "Opwarmen Bed" +#define UI_TEXT_KILLED "Uitgeschakeld" +#define UI_TEXT_STEPPER_DISABLED "Motor uitgezet" +#define UI_TEXT_EEPROM_STORED "Configuratie","saved. in EEPROM" +#define UI_TEXT_EEPROM_LOADED "Configuratie","loaded f. EEPROM" +#define UI_TEXT_UPLOADING "Uploaden..." +#define UI_TEXT_PAGE_BUFFER "Buffer:%oB" +#define UI_TEXT_PAGE_EXTRUDER "E:%ec/%Ec\002C\176%oC" +#define UI_TEXT_PAGE_EXTRUDER1 "E1:%e0/%E0\002C\176%o0" +#define UI_TEXT_PAGE_EXTRUDER2 "E2:%e1/%E1\002C\176%o1" +#define UI_TEXT_PAGE_BED "B:%eb/%Eb\002C\176%ob" +#define UI_TEXT_SPEED_MULTIPLY "Snelh. Mul.:%om%%%" +#define UI_TEXT_FLOW_MULTIPLY "Flow Mul.:%of%%%" + +#endif + +// ************************************************************************************* +// User defined language +// +// If you need a language not mentioned above, you can translate this dummy entry. +// If you want it added permanently to the distribution, spend it to the community under +// GPL V3. Only new and complete translations are put into the official distribution! +// ************************************************************************************* + +#if UI_LANGUAGE==1000 + +#define UI_TEXT_ON "On" +#define UI_TEXT_OFF "Off" +#define UI_TEXT_NA "N/A" // Output for not available +#define UI_TEXT_YES "Yes" +#define UI_TEXT_NO "No" +#define UI_TEXT_SEL "\003" +#define UI_TEXT_NOSEL "\004" +#define UI_TEXT_PRINT_POS "Printing..." +#define UI_TEXT_PRINTING "Printing" +#define UI_TEXT_IDLE "Idle" +#define UI_TEXT_NOSDCARD "No SD Card" +#define UI_TEXT_ERROR "**** ERROR ****" +#define UI_TEXT_BACK "Back \001" +#define UI_TEXT_QUICK_SETTINGS "Quick Settings" +#define UI_TEXT_CONFIGURATION "Configuration" +#define UI_TEXT_POSITION "Position" +#define UI_TEXT_EXTRUDER "Extruder" +#define UI_TEXT_SD_CARD "SD Card" +#define UI_TEXT_DEBUGGING "Debugging" +#define UI_TEXT_HOME_ALL "Home All" +#define UI_TEXT_HOME_X "Home X" +#define UI_TEXT_HOME_Y "Home Y" +#define UI_TEXT_HOME_Z "Home Z" +#define UI_TEXT_PREHEAT_PLA "Preheat PLA" +#define UI_TEXT_PREHEAT_ABS "Preheat ABS" +#define UI_TEXT_COOLDOWN "Cooldown" +#define UI_TEXT_SET_TO_ORIGIN "Set to Origin" +#define UI_TEXT_DISABLE_STEPPER "Disable stepper" +#define UI_TEXT_X_POSITION "X Position" +#define UI_TEXT_X_POS_FAST "X Pos. Fast" +#define UI_TEXT_Y_POSITION "Y Position" +#define UI_TEXT_Y_POS_FAST "Y Pos. Fast" +#define UI_TEXT_Z_POSITION "Z Position" +#define UI_TEXT_Z_POS_FAST "Z Pos. Fast" +#define UI_TEXT_E_POSITION "Extr. position" +#define UI_TEXT_BED_TEMP "Bed Temp.:%Eb\002C" +#define UI_TEXT_EXTR0_TEMP "Temp. 0 :%E0\002C" +#define UI_TEXT_EXTR1_TEMP "Temp. 1 :%E0\002C" +#define UI_TEXT_EXTR0_OFF "Extruder 0 Off" +#define UI_TEXT_EXTR1_OFF "Extruder 1 Off" +#define UI_TEXT_EXTR0_SELECT "%X0 Select Extr.0" +#define UI_TEXT_EXTR1_SELECT "%X1 Select Extr.1" +#define UI_TEXT_EXTR_ORIGIN "Set Origin" +#define UI_TEXT_PRINT_X "Print X:%ax" +#define UI_TEXT_PRINT_Y "Print Y:%ay" +#define UI_TEXT_PRINT_Z "Print Z:%az" +#define UI_TEXT_MOVE_X "Move X:%aX" +#define UI_TEXT_MOVE_Y "Move Y:%aY" +#define UI_TEXT_MOVE_Z "Move Z:%aZ" +#define UI_TEXT_JERK "Jerk:%aj" +#define UI_TEXT_ZJERK "Z-Jerk:%aJ" +#define UI_TEXT_ACCELERATION "Acceleration" +#define UI_TEXT_STORE_TO_EEPROM "Store to EEPROM" +#define UI_TEXT_LOAD_EEPROM "Load f. EEPROM" +#define UI_TEXT_DBG_ECHO "Echo :%do" +#define UI_TEXT_DBG_INFO "Info :%di" +#define UI_TEXT_DBG_ERROR "Errors :%de" +#define UI_TEXT_DBG_DRYRUN "Dry run:%dd" +#define UI_TEXT_OPS_OFF "%O0 OPS Off" +#define UI_TEXT_OPS_CLASSIC "%O1 OPS Classic" +#define UI_TEXT_OPS_FAST "%O2 OPS Fast" +#define UI_TEXT_OPS_RETRACT "Retract :%Or" +#define UI_TEXT_OPS_BACKSLASH "Backsl. :%Ob" +#define UI_TEXT_OPS_MINDIST "Min.dist:%Od" +#define UI_TEXT_OPS_MOVE_AFTER "Move after:%Oa" +#define UI_TEXT_ANTI_OOZE "Anti Ooze" +#define UI_TEXT_PRINT_FILE "Print file" +#define UI_TEXT_PAUSE_PRINT "Pause Print" +#define UI_TEXT_CONTINUE_PRINT "Continue Print" +#define UI_TEXT_UNMOUNT_CARD "Unmount Card" +#define UI_TEXT_MOUNT_CARD "Mount Card" +#define UI_TEXT_DELETE_FILE "Delete file" +#define UI_TEXT_FEEDRATE "Feedrate" +#define UI_TEXT_FEED_MAX_X "Max X:%fx" +#define UI_TEXT_FEED_MAX_Y "Max Y:%fy" +#define UI_TEXT_FEED_MAX_Z "Max Z:%fz" +#define UI_TEXT_FEED_HOME_X "Home X:%fX" +#define UI_TEXT_FEED_HOME_Y "Home Y:%fY" +#define UI_TEXT_FEED_HOME_Z "Home Z:%fZ" +#define UI_TEXT_ACTION_XPOSITION4 "X:%x0 mm","Min endstop:%sx","Max endstop:%sX","" +#define UI_TEXT_ACTION_YPOSITION4 "Y:%x1 mm","Min endstop:%sy","Max endstop:%sY","" +#define UI_TEXT_ACTION_ZPOSITION4 "Z:%x2 mm","Min endstop:%sz","Max endstop:%sZ","" +#define UI_TEXT_ACTION_XPOSITION_FAST4 "X:%x0 mm","Min endstop:%sx","Max endstop:%sX","" +#define UI_TEXT_ACTION_YPOSITION_FAST4 "Y:%x1 mm","Min endstop:%sy","Max endstop:%sY","" +#define UI_TEXT_ACTION_ZPOSITION_FAST4 "Z:%x2 mm","Min endstop:%sz","Max endstop:%sZ","" +#define UI_TEXT_ACTION_EPOSITION_FAST2 "E:%x3 mm","1 click = 1 mm" +#define UI_TEXT_ACTION_XPOSITION2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_ACTION_XPOSITION_FAST2 "X:%x0 mm","Min:%sx Max:%sX" +#define UI_TEXT_ACTION_YPOSITION_FAST2 "Y:%x1 mm","Min:%sy Max:%sY" +#define UI_TEXT_ACTION_ZPOSITION_FAST2 "Z:%x2 mm","Min:%sz Max:%sZ" +#define UI_TEXT_FANSPEED "Fan speed" +#define UI_TEXT_FAN_OFF "Turn Fan Off" +#define UI_TEXT_FAN_25 "Set Fan 25%%%" +#define UI_TEXT_FAN_50 "Set Fan 50%%%" +#define UI_TEXT_FAN_75 "Set Fan 75%%%" +#define UI_TEXT_FAN_FULL "Set Fan Full" +#define UI_TEXT_STEPPER_INACTIVE "Stepper Inactive" +#define UI_TEXT_STEPPER_INACTIVE2 "Dis. After:%is","[s] 0=Off" +#define UI_TEXT_POWER_INACTIVE "Max. Inactive" +#define UI_TEXT_POWER_INACTIVE2 "Dis. After:%ip","[s] 0=Off" +#define UI_TEXT_GENERAL "General" +#define UI_TEXT_BAUDRATE "Baudrate:%oc" +#define UI_TEXT_EXTR_STEPS "Steps/MM:%Se" +#define UI_TEXT_EXTR_START_FEED "Start FR:%Xf" +#define UI_TEXT_EXTR_MAX_FEED "Max FR:%XF" +#define UI_TEXT_EXTR_ACCEL "Accel:%XA" +#define UI_TEXT_EXTR_WATCH "Stab.Time:%Xw" +#define UI_TEXT_EXTR_ADVANCE_L "Advance lin:Xl%" +#define UI_TEXT_EXTR_ADVANCE_K "Advance quad:Xa%" +#define UI_TEXT_EXTR_MANAGER "Control:%Xh" +#define UI_TEXT_EXTR_PGAIN "PID P:%Xp" +#define UI_TEXT_EXTR_IGAIN "PID I:%Xi" +#define UI_TEXT_EXTR_DGAIN "PID D:%Xd" +#define UI_TEXT_EXTR_DMIN "Drive Min:%Xm" +#define UI_TEXT_EXTR_DMAX "Drive Max:%XM" +#define UI_TEXT_EXTR_PMAX "PID Max:%XD" +#define UI_TEXT_EXTR_XOFF "X-Offset:%Xx" +#define UI_TEXT_EXTR_YOFF "Y-Offset:%Xy" +#define UI_TEXT_STRING_HM_BANGBANG "BangBang" +#define UI_TEXT_STRING_HM_PID "PID" +#define UI_TEXT_STRING_ACTION "Action:%la" +#define UI_TEXT_HEATING_EXTRUDER "Heating Extruder" +#define UI_TEXT_HEATING_BED "Heating Bed" +#define UI_TEXT_KILLED "Killed" +#define UI_TEXT_STEPPER_DISABLED "Stepper Disabled" +#define UI_TEXT_EEPROM_STORED "Configuration","stored in EEPROM" +#define UI_TEXT_EEPROM_LOADED "Configuration","loaded f. EEPROM" +#define UI_TEXT_UPLOADING "Uploading..." +#define UI_TEXT_PAGE_BUFFER "Buffer:%oB" +#define UI_TEXT_PAGE_EXTRUDER "E:%ec/%Ec\002C\176%oC" +#define UI_TEXT_PAGE_EXTRUDER1 "E1:%e0/%E0\002C\176%o0" +#define UI_TEXT_PAGE_EXTRUDER2 "E2:%e1/%E1\002C\176%o1" +#define UI_TEXT_PAGE_BED "B:%eb/%Eb\002C\176%ob" +#define UI_TEXT_SPEED_MULTIPLY "Speed Mul.:%om%%%" +#define UI_TEXT_FLOW_MULTIPLY "Flow Mul.:%of%%%" +#define UI_TEXT_EXTR_WAIT_RETRACT_TEMP "Wait Temp.%XT\002C" +#define UI_TEXT_EXTR_WAIT_RETRACT_UNITS "Wait Units:%XUmm" +#define UI_TEXT_PRINTER_READY "Printer ready." + +#endif diff --git a/Repetier/uimenu.h b/Repetier/uimenu.h new file mode 100644 index 0000000..0c3086e --- /dev/null +++ b/Repetier/uimenu.h @@ -0,0 +1,505 @@ +/* + This file is part of Repetier-Firmware. + + Repetier-Firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Repetier-Firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Repetier-Firmware. If not, see . + +*/ +#if !defined(_UI_MENU_H) && defined(UI_MAIN) +#define _UI_MENU_H + +/* +The menu configuration uses dynamic strings. These dynamic strings can contain +a placeholder for special values. During print these placeholder are exchanged +by their current value. Everything else is printed exactly as written. + +A placeholder always has 3 chars. It starts with a % followed by 2 characters +defining the value. You can use any placeholder in any position, also it doesn't +always make sense. + +List of placeholder: +%ec : Current extruder temperature +%eb : Current heated bed temperature +%e0..9 : Temp. of extruder 0..9 +%er : Extruder relative mode +%Ec : Target temperature of current extruder +%Eb : Target temperature of heated bed +%E0-9 : Target temperature of extruder 0..9 +%os : Status message +%oe : Error message +%oB : Buffer length +%om : Speed multiplier +%of : flow multiplier +%oc : Connection baudrate +%o0..9 : Output level extruder 0..9 is % including %sign. +%oC : Output level current extruder +%ob : Output level heated bed +%%% : The % char +%x0 : X position +%x1 : Y position +%x2 : Z position +%x3 : Current extruder position +%sx : State of x min endstop. +%sX : State of x max endstop. +%sy : State of y min endstop. +%sY : State of y max endstop. +%sz : State of z min endstop. +%sZ : State of z max endstop. +%do : Debug echo state. +%di : Debug info state. +%de : Debug error state. +%dd : Debug dry run state. +%O0 : OPS mode = 0 +%O1 : OPS mode = 1 +%O2 : OPS mode = 2 +%Or : OPS retract distance +%Ob : OPS backslash distance +%Od : OPS min distance +%Oa : OPS move after +%ax : X acceleration during print moves +%ay : Y acceleration during print moves +%az : Z acceleration during print moves +%aX : X acceleration during travel moves +%aY : Y acceleration during travel moves +%aZ : Z acceleration during travel moves +%aj : Max. jerk +%aJ : Max. Z-jerk +%fx : Max. feedrate x direction +%fy : Max. feedrate y direction +%fz : Max. feedrate z direction +%fe : Max. feedrate current extruder +%fX : Homing feedrate x direction +%fY : Homing feedrate y direction +%fZ : Homing feedrate z direction +%Sx : Steps per mm x direction +%Sy : Steps per mm y direction +%Sz : Steps per mm z direction +%Se : Steps per mm current extruder +%is : Stepper inactive time in seconds +%ip : Max. inactive time in seconds +%X0..9 : Extruder selected marker +%Xi : PID I gain +%Xp : PID P gain +%Xd : PID D gain +%Xm : PID drive min +%XM : PID drive max +%XD : PID max +%Xw : Extruder watch period in seconds +%Xh : Extruder heat manager (BangBang/PID) +%Xa : Advance K value +%Xx : x offset in steps +%Xy : y offset in steps +%Xf : Extruder max. start feedrate +%XF : Extruder max. feedrate +%XA : Extruder max. acceleration +*/ + + +// Define precision for temperatures. With small displays only integer values fit. +#ifndef UI_TEMP_PRECISION +#if UI_COLS>16 +#define UI_TEMP_PRECISION 1 +#else +#define UI_TEMP_PRECISION 0 +#endif +#endif + +/* ============= PAGES DEFINITION ============= + +If you are not iside a menu, the firmware displays pages with information. +Especially if you have only a small display it is convenient to have +more then one information page. +*/ + +/* Define your pages using dynamic strings. To define a page use +UI_PAGE4(name,row1,row2,row3,row4); +for 4 row displays and +UI_PAGE2(name,row1,row2); +for 2 row displays. You can add additional pages or change the default pages like you want. +*/ +#if UI_ROWS>=4 + #if HAVE_HEATED_BED==true + UI_PAGE4(ui_page1,"\005%ec/%Ec\002B%eB/%Eb\002","Z:%x2","Mul:%om Buf:%oB","%os"); + //UI_PAGE4(ui_page1,UI_TEXT_PAGE_EXTRUDER,UI_TEXT_PAGE_BED,UI_TEXT_PAGE_BUFFER,"%os"); + #else + UI_PAGE4(ui_page1,UI_TEXT_PAGE_EXTRUDER,"Z:%x2 mm",UI_TEXT_PAGE_BUFFER,"%os"); + #endif + UI_PAGE4(ui_page2,"X:%x0 mm","Y:%x1 mm","Z:%x2 mm","%os"); +//UI_PAGE4(ui_page2,"dX:%y0 mm %sX","dY:%y1 mm %sY","dZ:%y2 mm %sZ","%os"); + UI_PAGE4(ui_page3,UI_TEXT_PAGE_EXTRUDER1, +#if NUM_EXTRUDER>1 +UI_TEXT_PAGE_EXTRUDER2 +#else + "" +#endif + ,UI_TEXT_PAGE_BED,"%os"); + +/* +Merge pages together. Use the following pattern: +#define UI_PAGES {&name1,&name2,&name3} +*/ +#define UI_PAGES {&ui_page1,&ui_page2,&ui_page3} +// How many pages do you want to have. Minimum is 1. +#define UI_NUM_PAGES 3 +#else +#if HAVE_HEATED_BED==true +UI_PAGE2(ui_page1,UI_TEXT_PAGE_EXTRUDER,UI_TEXT_PAGE_BED); +#else +UI_PAGE2(ui_page1,UI_TEXT_PAGE_EXTRUDER,"%os"); +#endif +UI_PAGE2(ui_page2,"X:%x0 Y:%x1","%os"); +UI_PAGE2(ui_page3,"Z:%x2 mm","%os"); +/* +Merge pages together. Use the following pattern: +#define UI_PAGES {&name1,&name2,&name3} +*/ +#define UI_PAGES {&ui_page1,&ui_page2,&ui_page3} +// How many pages do you want to have. Minimum is 1. +#define UI_NUM_PAGES 3 +#endif +/* ============ MENU definition ================ + +The menu works the same as pages. In addion you need to define what the lines do +or where to jump to. For that reason, the menu structure needs to be entered in +reverse order. Starting from the leaves, the menu structure is defined. +*/ + +/* +At first define all actions available in the menu. The actions define, what the +next/previous button will do. All menu actions work the same: +next/previous changes the value +ok sets the value if not already done and goes back to previous menu. +*/ + +// Error menu + +UI_MENU_ACTION2(ui_menu_error,UI_ACTION_DUMMY,UI_TEXT_ERROR,"%oe"); + +// **** Positions submenus + +#if UI_ROWS>=4 +UI_MENU_ACTION4C(ui_menu_xpos,UI_ACTION_XPOSITION,UI_TEXT_ACTION_XPOSITION4); +UI_MENU_ACTION4C(ui_menu_ypos,UI_ACTION_YPOSITION,UI_TEXT_ACTION_YPOSITION4); +UI_MENU_ACTION4C(ui_menu_zpos,UI_ACTION_ZPOSITION,UI_TEXT_ACTION_ZPOSITION4); +UI_MENU_ACTION4C(ui_menu_xpos_fast,UI_ACTION_XPOSITION_FAST,UI_TEXT_ACTION_XPOSITION_FAST4); +UI_MENU_ACTION4C(ui_menu_ypos_fast,UI_ACTION_YPOSITION_FAST,UI_TEXT_ACTION_YPOSITION_FAST4); +UI_MENU_ACTION4C(ui_menu_zpos_fast,UI_ACTION_ZPOSITION_FAST,UI_TEXT_ACTION_ZPOSITION_FAST4); +#else +UI_MENU_ACTION2C(ui_menu_xpos,UI_ACTION_XPOSITION,UI_TEXT_ACTION_XPOSITION2); +UI_MENU_ACTION2C(ui_menu_ypos,UI_ACTION_YPOSITION,UI_TEXT_ACTION_YPOSITION2); +UI_MENU_ACTION2C(ui_menu_zpos,UI_ACTION_ZPOSITION,UI_TEXT_ACTION_ZPOSITION2); +UI_MENU_ACTION2C(ui_menu_xpos_fast,UI_ACTION_XPOSITION_FAST,UI_TEXT_ACTION_XPOSITION_FAST2); +UI_MENU_ACTION2C(ui_menu_ypos_fast,UI_ACTION_YPOSITION_FAST,UI_TEXT_ACTION_YPOSITION_FAST2); +UI_MENU_ACTION2C(ui_menu_zpos_fast,UI_ACTION_ZPOSITION_FAST,UI_TEXT_ACTION_ZPOSITION_FAST2); +#endif +UI_MENU_ACTION2C(ui_menu_epos,UI_ACTION_EPOSITION,UI_TEXT_ACTION_EPOSITION_FAST2); + +/* +Next step is to define submenus leading to the action. +*/ + +// **** Positionening menu +UI_MENU_ACTIONCOMMAND(ui_menu_back,UI_TEXT_BACK,UI_ACTION_BACK); +#if UI_HAS_BACK_KEY==0 +#define UI_MENU_ADDCONDBACK &ui_menu_back, +#define UI_MENU_BACKCNT 1 +#else +#define UI_MENU_ADDCONDBACK +#define UI_MENU_BACKCNT 0 +#endif +UI_MENU_ACTIONCOMMAND(ui_menu_home_all,UI_TEXT_HOME_ALL,UI_ACTION_HOME_ALL); +UI_MENU_ACTIONCOMMAND(ui_menu_home_x,UI_TEXT_HOME_X,UI_ACTION_HOME_X); +UI_MENU_ACTIONCOMMAND(ui_menu_home_y,UI_TEXT_HOME_Y,UI_ACTION_HOME_Y); +UI_MENU_ACTIONCOMMAND(ui_menu_home_z,UI_TEXT_HOME_Z,UI_ACTION_HOME_Z); +UI_MENU_ACTIONSELECTOR(ui_menu_go_xpos,UI_TEXT_X_POSITION,ui_menu_xpos); +UI_MENU_ACTIONSELECTOR(ui_menu_go_ypos,UI_TEXT_Y_POSITION,ui_menu_ypos); +UI_MENU_ACTIONSELECTOR(ui_menu_go_zpos,UI_TEXT_Z_POSITION,ui_menu_zpos); +UI_MENU_ACTIONSELECTOR(ui_menu_go_epos,UI_TEXT_E_POSITION,ui_menu_epos); +UI_MENU_ACTIONSELECTOR(ui_menu_go_xfast,UI_TEXT_X_POS_FAST,ui_menu_xpos_fast); +UI_MENU_ACTIONSELECTOR(ui_menu_go_yfast,UI_TEXT_Y_POS_FAST,ui_menu_ypos_fast); +UI_MENU_ACTIONSELECTOR(ui_menu_go_zfast,UI_TEXT_Z_POS_FAST,ui_menu_zpos_fast); + +#define UI_MENU_POSITIONS {UI_MENU_ADDCONDBACK &ui_menu_home_all,&ui_menu_home_x,&ui_menu_home_y,&ui_menu_home_z,&ui_menu_go_xfast,&ui_menu_go_xpos,&ui_menu_go_yfast,&ui_menu_go_ypos,&ui_menu_go_zfast,&ui_menu_go_zpos,&ui_menu_go_epos} +UI_MENU(ui_menu_positions,UI_MENU_POSITIONS,11+UI_MENU_BACKCNT); + +// **** Delta calibration menu +#ifdef STEP_COUNTER +UI_MENU_ACTIONCOMMAND(ui_menu_show_measurement,UI_TEXT_SHOW_MEASUREMENT,UI_ACTION_SHOW_MEASUREMENT); +UI_MENU_ACTIONCOMMAND(ui_menu_reset_measurement,UI_TEXT_RESET_MEASUREMENT,UI_ACTION_RESET_MEASUREMENT); +UI_MENU_ACTIONCOMMAND(ui_menu_set_measured_origin,UI_TEXT_SET_MEASURED_ORIGIN,UI_ACTION_SET_MEASURED_ORIGIN); +#define UI_MENU_DELTA {UI_MENU_ADDCONDBACK &ui_menu_show_measurement,&ui_menu_reset_measurement,&ui_menu_set_measured_origin,&ui_menu_home_all,&ui_menu_go_zpos,&ui_menu_go_zfast} +UI_MENU(ui_menu_delta,UI_MENU_DELTA,6+UI_MENU_BACKCNT); +#endif + +// **** Bed leveling menu +#ifdef SOFTWARE_LEVELING +UI_MENU_ACTIONCOMMAND(ui_menu_set_p1,UI_TEXT_SET_P1,UI_ACTION_SET_P1); +UI_MENU_ACTIONCOMMAND(ui_menu_set_p2,UI_TEXT_SET_P2,UI_ACTION_SET_P2); +UI_MENU_ACTIONCOMMAND(ui_menu_set_p3,UI_TEXT_SET_P3,UI_ACTION_SET_P3); +UI_MENU_ACTIONCOMMAND(ui_menu_calculate_leveling,UI_TEXT_CALCULATE_LEVELING,UI_ACTION_CALC_LEVEL); +#define UI_MENU_LEVEL {UI_MENU_ADDCONDBACK &ui_menu_set_p1,&ui_menu_set_p2,&ui_menu_set_p3,&ui_menu_calculate_leveling,&ui_menu_go_xpos,&ui_menu_go_xfast,&ui_menu_go_ypos,&ui_menu_go_yfast,&ui_menu_go_zpos,&ui_menu_go_zfast} +UI_MENU(ui_menu_level,UI_MENU_LEVEL,10+UI_MENU_BACKCNT); +#endif + +// **** Extruder menu + +UI_MENU_CHANGEACTION(ui_menu_ext_temp0,UI_TEXT_EXTR0_TEMP,UI_ACTION_EXTRUDER0_TEMP); +UI_MENU_CHANGEACTION(ui_menu_ext_temp1,UI_TEXT_EXTR1_TEMP,UI_ACTION_EXTRUDER1_TEMP); +UI_MENU_CHANGEACTION(ui_menu_bed_temp, UI_TEXT_BED_TEMP,UI_ACTION_HEATED_BED_TEMP); +UI_MENU_ACTIONCOMMAND(ui_menu_ext_sel0,UI_TEXT_EXTR0_SELECT,UI_ACTION_SELECT_EXTRUDER0); +UI_MENU_ACTIONCOMMAND(ui_menu_ext_sel1,UI_TEXT_EXTR1_SELECT,UI_ACTION_SELECT_EXTRUDER1); +UI_MENU_ACTIONCOMMAND(ui_menu_ext_off0,UI_TEXT_EXTR0_OFF,UI_ACTION_EXTRUDER0_OFF); +UI_MENU_ACTIONCOMMAND(ui_menu_ext_off1,UI_TEXT_EXTR1_OFF,UI_ACTION_EXTRUDER1_OFF); +UI_MENU_ACTIONCOMMAND(ui_menu_ext_origin,UI_TEXT_EXTR_ORIGIN,UI_ACTION_RESET_EXTRUDER); + +#if NUM_EXTRUDER>1 +#define UI_MENU_EXTCOND &ui_menu_ext_temp0,&ui_menu_ext_temp1,&ui_menu_ext_off0,&ui_menu_ext_off1,&ui_menu_ext_sel0,&ui_menu_ext_sel1, +#define UI_MENU_EXTCNT 6 +#else +#define UI_MENU_EXTCOND &ui_menu_ext_temp0,&ui_menu_ext_off0, +#define UI_MENU_EXTCNT 2 +#endif +#if HAVE_HEATED_BED==true +#define UI_MENU_BEDCOND &ui_menu_bed_temp, +#define UI_MENU_BEDCNT 1 +#else +#define UI_MENU_BEDCOND +#define UI_MENU_BEDCNT 0 +#endif + +#define UI_MENU_EXTRUDER {UI_MENU_ADDCONDBACK UI_MENU_BEDCOND UI_MENU_EXTCOND &ui_menu_go_epos,&ui_menu_ext_origin} +UI_MENU(ui_menu_extruder,UI_MENU_EXTRUDER,UI_MENU_BACKCNT+UI_MENU_BEDCNT+UI_MENU_EXTCNT+2); + +// **** SD card menu + +// **** Quick menu +UI_MENU_ACTIONCOMMAND(ui_menu_quick_preheat_pla,UI_TEXT_PREHEAT_PLA,UI_ACTION_PREHEAT_PLA); +UI_MENU_ACTIONCOMMAND(ui_menu_quick_preheat_abs,UI_TEXT_PREHEAT_ABS,UI_ACTION_PREHEAT_ABS); +UI_MENU_ACTIONCOMMAND(ui_menu_quick_cooldown,UI_TEXT_COOLDOWN,UI_ACTION_COOLDOWN); +UI_MENU_ACTIONCOMMAND(ui_menu_quick_origin,UI_TEXT_SET_TO_ORIGIN,UI_ACTION_SET_ORIGIN); +UI_MENU_ACTIONCOMMAND(ui_menu_quick_stopstepper,UI_TEXT_DISABLE_STEPPER,UI_ACTION_DISABLE_STEPPER); +UI_MENU_CHANGEACTION(ui_menu_quick_speedmultiply,UI_TEXT_SPEED_MULTIPLY,UI_ACTION_FEEDRATE_MULTIPLY); +UI_MENU_CHANGEACTION(ui_menu_quick_flowmultiply,UI_TEXT_FLOW_MULTIPLY,UI_ACTION_FLOWRATE_MULTIPLY); +#define UI_MENU_QUICK {UI_MENU_ADDCONDBACK &ui_menu_home_all,&ui_menu_quick_preheat_pla,&ui_menu_quick_preheat_abs,&ui_menu_quick_speedmultiply,&ui_menu_quick_flowmultiply,&ui_menu_quick_cooldown,&ui_menu_quick_origin,&ui_menu_quick_stopstepper} +UI_MENU(ui_menu_quick,UI_MENU_QUICK,8+UI_MENU_BACKCNT); + +// **** Fan menu + +#if FAN_PIN>-1 +UI_MENU_ACTIONCOMMAND(ui_menu_fan_off,UI_TEXT_FAN_OFF,UI_ACTION_FAN_OFF); +UI_MENU_ACTIONCOMMAND(ui_menu_fan_25,UI_TEXT_FAN_25,UI_ACTION_FAN_25); +UI_MENU_ACTIONCOMMAND(ui_menu_fan_50,UI_TEXT_FAN_50,UI_ACTION_FAN_50); +UI_MENU_ACTIONCOMMAND(ui_menu_fan_75,UI_TEXT_FAN_75,UI_ACTION_FAN_75); +UI_MENU_ACTIONCOMMAND(ui_menu_fan_full,UI_TEXT_FAN_FULL,UI_ACTION_FAN_FULL); +#define UI_MENU_FAN {UI_MENU_ADDCONDBACK &ui_menu_fan_off,&ui_menu_fan_25,&ui_menu_fan_50,&ui_menu_fan_75,&ui_menu_fan_full} +UI_MENU(ui_menu_fan,UI_MENU_FAN,5+UI_MENU_BACKCNT); +UI_MENU_SUBMENU(ui_menu_fan_sub,UI_TEXT_FANSPEED,ui_menu_fan); +#define UI_MENU_FAN_COND &ui_menu_fan_sub, +#define UI_MENU_FAN_CNT 1 +#else +#define UI_MENU_FAN_COND +#define UI_MENU_FAN_CNT 0 +#endif + +// **** SD card menu + +#ifdef SDSUPPORT +#define UI_MENU_SD_FILESELECTOR {&ui_menu_back} +UI_MENU_FILESELECT(ui_menu_sd_fileselector,UI_MENU_SD_FILESELECTOR,1); +UI_MENU_ACTIONCOMMAND(ui_menu_sd_printfile,UI_TEXT_PRINT_FILE,UI_ACTION_SD_PRINT); +UI_MENU_ACTIONCOMMAND(ui_menu_sd_pause,UI_TEXT_PAUSE_PRINT,UI_ACTION_SD_PAUSE); +UI_MENU_ACTIONCOMMAND(ui_menu_sd_continue,UI_TEXT_CONTINUE_PRINT,UI_ACTION_SD_CONTINUE); +UI_MENU_ACTIONCOMMAND(ui_menu_sd_unmount,UI_TEXT_UNMOUNT_CARD,UI_ACTION_SD_UNMOUNT); +UI_MENU_ACTIONCOMMAND(ui_menu_sd_mount,UI_TEXT_MOUNT_CARD,UI_ACTION_SD_MOUNT); +UI_MENU_ACTIONCOMMAND(ui_menu_sd_delete,UI_TEXT_DELETE_FILE,UI_ACTION_SD_DELETE); +#define UI_MENU_SD {UI_MENU_ADDCONDBACK &ui_menu_sd_printfile,&ui_menu_sd_pause,&ui_menu_sd_continue,&ui_menu_sd_unmount,&ui_menu_sd_mount,&ui_menu_sd_delete} +UI_MENU(ui_menu_sd,UI_MENU_SD,UI_MENU_BACKCNT+6); +UI_MENU_SUBMENU(ui_menu_sd_sub,UI_TEXT_SD_CARD,ui_menu_sd); + +#define UI_MENU_SD_COND &ui_menu_sd_sub, +#define UI_MENU_SD_CNT 1 +#else +#define UI_MENU_SD_COND +#define UI_MENU_SD_CNT 0 +#endif + + +// **** Debugging menu +UI_MENU_ACTIONCOMMAND(ui_menu_debug_echo,UI_TEXT_DBG_ECHO,UI_ACTION_DEBUG_ECHO); +UI_MENU_ACTIONCOMMAND(ui_menu_debug_info,UI_TEXT_DBG_INFO,UI_ACTION_DEBUG_INFO); +UI_MENU_ACTIONCOMMAND(ui_menu_debug_error,UI_TEXT_DBG_ERROR,UI_ACTION_DEBUG_ERROR); +UI_MENU_ACTIONCOMMAND(ui_menu_debug_dryrun,UI_TEXT_DBG_DRYRUN,UI_ACTION_DEBUG_DRYRUN); + +#define UI_MENU_DEBUGGING {UI_MENU_ADDCONDBACK &ui_menu_debug_echo,&ui_menu_debug_info,&ui_menu_debug_error,&ui_menu_debug_dryrun} +UI_MENU(ui_menu_debugging,UI_MENU_DEBUGGING,4+UI_MENU_BACKCNT); + +// **** OPS configuration +#if USE_OPS==1 +UI_MENU_ACTIONCOMMAND(ui_menu_ops_off,UI_TEXT_OPS_OFF,UI_ACTION_OPS_OFF); +UI_MENU_ACTIONCOMMAND(ui_menu_ops_classic,UI_TEXT_OPS_CLASSIC,UI_ACTION_OPS_CLASSIC); +UI_MENU_ACTIONCOMMAND(ui_menu_ops_fast,UI_TEXT_OPS_FAST,UI_ACTION_OPS_FAST); +UI_MENU_CHANGEACTION(ui_menu_ops_retract,UI_TEXT_OPS_RETRACT,UI_ACTION_OPS_RETRACTDISTANCE); +UI_MENU_CHANGEACTION(ui_menu_ops_backslash,UI_TEXT_OPS_BACKSLASH,UI_ACTION_OPS_BACKLASH); +UI_MENU_CHANGEACTION(ui_menu_ops_mindist,UI_TEXT_OPS_MINDIST,UI_ACTION_OPS_MINDISTANCE); +UI_MENU_CHANGEACTION(ui_menu_ops_moveafter,UI_TEXT_OPS_MOVE_AFTER,UI_ACTION_OPS_MOVE_AFTER); +#define UI_MENU_OPS {UI_MENU_ADDCONDBACK &ui_menu_ops_off,&ui_menu_ops_classic,&ui_menu_ops_fast,&ui_menu_ops_retract,&ui_menu_ops_backslash,&ui_menu_ops_mindist,&ui_menu_ops_moveafter} +UI_MENU(ui_menu_ops,UI_MENU_OPS,7+UI_MENU_BACKCNT); +UI_MENU_SUBMENU(ui_menu_ops_sub,UI_TEXT_ANTI_OOZE,ui_menu_ops); +#define UI_MENU_ADDCONDOPS &ui_menu_ops_sub, +#else +#define UI_MENU_ADDCONDOPS +#endif + +// **** Acceleration settings +UI_MENU_CHANGEACTION(ui_menu_accel_printx,UI_TEXT_PRINT_X,UI_ACTION_PRINT_ACCEL_X); +UI_MENU_CHANGEACTION(ui_menu_accel_printy,UI_TEXT_PRINT_Y,UI_ACTION_PRINT_ACCEL_Y); +UI_MENU_CHANGEACTION(ui_menu_accel_printz,UI_TEXT_PRINT_Z,UI_ACTION_PRINT_ACCEL_Z); +UI_MENU_CHANGEACTION(ui_menu_accel_travelx,UI_TEXT_MOVE_X,UI_ACTION_MOVE_ACCEL_X); +UI_MENU_CHANGEACTION(ui_menu_accel_travely,UI_TEXT_MOVE_Y,UI_ACTION_MOVE_ACCEL_Y); +UI_MENU_CHANGEACTION(ui_menu_accel_travelz,UI_TEXT_MOVE_Z,UI_ACTION_MOVE_ACCEL_Z); +UI_MENU_CHANGEACTION(ui_menu_accel_jerk,UI_TEXT_JERK,UI_ACTION_MAX_JERK); +UI_MENU_CHANGEACTION(ui_menu_accel_zjerk,UI_TEXT_ZJERK,UI_ACTION_MAX_ZJERK); +#define UI_MENU_ACCEL {UI_MENU_ADDCONDBACK &ui_menu_accel_printx,&ui_menu_accel_printy,&ui_menu_accel_printz,&ui_menu_accel_travelx,&ui_menu_accel_travely,&ui_menu_accel_travelz,&ui_menu_accel_jerk,&ui_menu_accel_zjerk} +UI_MENU(ui_menu_accel,UI_MENU_ACCEL,8+UI_MENU_BACKCNT); + +// **** Feedrates +UI_MENU_CHANGEACTION(ui_menu_feedrate_maxx,UI_TEXT_FEED_MAX_X,UI_ACTION_MAX_FEEDRATE_X); +UI_MENU_CHANGEACTION(ui_menu_feedrate_maxy,UI_TEXT_FEED_MAX_Y,UI_ACTION_MAX_FEEDRATE_Y); +UI_MENU_CHANGEACTION(ui_menu_feedrate_maxz,UI_TEXT_FEED_MAX_Z,UI_ACTION_MAX_FEEDRATE_Z); +UI_MENU_CHANGEACTION(ui_menu_feedrate_homex,UI_TEXT_FEED_HOME_X,UI_ACTION_HOMING_FEEDRATE_X); +UI_MENU_CHANGEACTION(ui_menu_feedrate_homey,UI_TEXT_FEED_HOME_Y,UI_ACTION_HOMING_FEEDRATE_Y); +UI_MENU_CHANGEACTION(ui_menu_feedrate_homez,UI_TEXT_FEED_HOME_Z,UI_ACTION_HOMING_FEEDRATE_Z); +#define UI_MENU_FEEDRATE {UI_MENU_ADDCONDBACK &ui_menu_feedrate_maxx,&ui_menu_feedrate_maxy,&ui_menu_feedrate_maxz,&ui_menu_feedrate_homex,&ui_menu_feedrate_homey,&ui_menu_feedrate_homez} +UI_MENU(ui_menu_feedrate,UI_MENU_ACCEL,6+UI_MENU_BACKCNT); + +// **** General configuration settings + +UI_MENU_ACTION2C(ui_menu_stepper2,UI_ACTION_STEPPER_INACTIVE,UI_TEXT_STEPPER_INACTIVE2); +UI_MENU_ACTION2C(ui_menu_maxinactive2,UI_ACTION_MAX_INACTIVE,UI_TEXT_POWER_INACTIVE2); +UI_MENU_CHANGEACTION(ui_menu_general_baud,UI_TEXT_BAUDRATE,UI_ACTION_BAUDRATE); +UI_MENU_ACTIONSELECTOR(ui_menu_general_stepper_inactive,UI_TEXT_STEPPER_INACTIVE,ui_menu_stepper2); +UI_MENU_ACTIONSELECTOR(ui_menu_general_max_inactive,UI_TEXT_POWER_INACTIVE,ui_menu_maxinactive2); +#define UI_MENU_GENERAL {UI_MENU_ADDCONDBACK &ui_menu_general_baud,&ui_menu_general_stepper_inactive,&ui_menu_general_max_inactive} +UI_MENU(ui_menu_general,UI_MENU_GENERAL,3+UI_MENU_BACKCNT); + +// **** Extruder configuration + +UI_MENU_CHANGEACTION(ui_menu_cext_steps,UI_TEXT_EXTR_STEPS,UI_ACTION_EXTR_STEPS); +UI_MENU_CHANGEACTION(ui_menu_cext_start_feedrate,UI_TEXT_EXTR_START_FEED,UI_ACTION_EXTR_START_FEEDRATE); +UI_MENU_CHANGEACTION(ui_menu_cext_max_feedrate,UI_TEXT_EXTR_MAX_FEED,UI_ACTION_EXTR_MAX_FEEDRATE); +UI_MENU_CHANGEACTION(ui_menu_cext_acceleration,UI_TEXT_EXTR_ACCEL,UI_ACTION_EXTR_ACCELERATION); +UI_MENU_CHANGEACTION(ui_menu_cext_watch_period,UI_TEXT_EXTR_WATCH,UI_ACTION_EXTR_WATCH_PERIOD); +UI_MENU_CHANGEACTION(ui_menu_ext_wait_temp,UI_TEXT_EXTR_WAIT_RETRACT_TEMP,UI_ACTION_EXTR_WAIT_RETRACT_TEMP); +UI_MENU_CHANGEACTION(ui_menu_ext_wait_units,UI_TEXT_EXTR_WAIT_RETRACT_UNITS,UI_ACTION_EXTR_WAIT_RETRACT_UNITS); +#define UI_MENU_ADV_CNT 0 +#define UI_MENU_ADVANCE +#ifdef USE_ADVANCE +#define UI_MENU_ADV_CNT 1 +#define UI_MENU_ADVANCE ,&ui_menu_cext_advancel +#ifdef ENABLE_QUADRATIC_ADVANCE +#define UI_MENU_ADV_CNT 2 +#define UI_MENU_ADVANCE ,&ui_menu_cext_advancel,&ui_menu_cext_advancek +UI_MENU_CHANGEACTION(ui_menu_cext_advancek,UI_TEXT_EXTR_ADVANCE_K,UI_ACTION_ADVANCE_K); +#endif +UI_MENU_CHANGEACTION(ui_menu_cext_advancel,UI_TEXT_EXTR_ADVANCE_L,UI_ACTION_ADVANCE_L); +#endif +#ifdef TEMP_PID +UI_MENU_CHANGEACTION(ui_menu_cext_manager,UI_TEXT_EXTR_MANAGER,UI_ACTION_EXTR_HEATMANAGER); +UI_MENU_CHANGEACTION(ui_menu_cext_pgain,UI_TEXT_EXTR_PGAIN,UI_ACTION_PID_PGAIN); +UI_MENU_CHANGEACTION(ui_menu_cext_igain,UI_TEXT_EXTR_IGAIN,UI_ACTION_PID_IGAIN); +UI_MENU_CHANGEACTION(ui_menu_cext_dgain,UI_TEXT_EXTR_DGAIN,UI_ACTION_PID_DGAIN); +UI_MENU_CHANGEACTION(ui_menu_cext_dmin,UI_TEXT_EXTR_DMIN,UI_ACTION_DRIVE_MIN); +UI_MENU_CHANGEACTION(ui_menu_cext_dmax,UI_TEXT_EXTR_DMAX,UI_ACTION_DRIVE_MAX); +UI_MENU_CHANGEACTION(ui_menu_cext_pmax,UI_TEXT_EXTR_PMAX,UI_ACTION_PID_MAX); +#define UI_MENU_PIDCOND ,&ui_menu_cext_manager,&ui_menu_cext_pgain,&ui_menu_cext_igain,&ui_menu_cext_dgain,&ui_menu_cext_dmin,&ui_menu_cext_dmax,&ui_menu_cext_pmax +#define UI_MENU_PIDCNT 7 +#else +#define UI_MENU_PIDCOND +#define UI_MENU_PIDCNT 0 +#endif +#if NUM_EXTRUDER>1 +UI_MENU_CHANGEACTION(ui_menu_cext_xoffset,UI_TEXT_EXTR_XOFF,UI_ACTION_X_OFFSET); +UI_MENU_CHANGEACTION(ui_menu_cext_yoffset,UI_TEXT_EXTR_YOFF,UI_ACTION_Y_OFFSET); +#define UI_MENU_CONFEXTCOND &ui_menu_ext_sel0,&ui_menu_ext_sel1,&ui_menu_cext_xoffset,&ui_menu_cext_yoffset, +#define UI_MENU_CONFEXTCNT 4 +#else +#define UI_MENU_CONFEXTCOND +#define UI_MENU_CONFEXTCNT 0 +#endif +#define UI_MENU_CEXTR {UI_MENU_ADDCONDBACK UI_MENU_CONFEXTCOND &ui_menu_cext_steps,&ui_menu_cext_start_feedrate,&ui_menu_cext_max_feedrate,&ui_menu_cext_acceleration,&ui_menu_cext_watch_period,&ui_menu_ext_wait_units,&ui_menu_ext_wait_temp UI_MENU_ADVANCE UI_MENU_PIDCOND} +UI_MENU(ui_menu_cextr,UI_MENU_CEXTR,7+UI_MENU_BACKCNT+UI_MENU_PIDCNT+UI_MENU_CONFEXTCNT+UI_MENU_ADV_CNT); + +// **** Configuration menu +UI_MENU_SUBMENU(ui_menu_conf_general,UI_TEXT_GENERAL,ui_menu_general); +UI_MENU_SUBMENU(ui_menu_conf_accel,UI_TEXT_ACCELERATION,ui_menu_accel); +UI_MENU_SUBMENU(ui_menu_conf_feed,UI_TEXT_FEEDRATE,ui_menu_feedrate); +UI_MENU_SUBMENU(ui_menu_conf_extr,UI_TEXT_EXTRUDER,ui_menu_cextr); +#if EEPROM_MODE!=0 +UI_MENU_ACTIONCOMMAND(ui_menu_conf_to_eeprom,UI_TEXT_STORE_TO_EEPROM,UI_ACTION_STORE_EEPROM); +UI_MENU_ACTIONCOMMAND(ui_menu_conf_from_eeprom,UI_TEXT_LOAD_EEPROM,UI_ACTION_LOAD_EEPROM); +#define UI_MENU_EEPROM_COND ,&ui_menu_conf_to_eeprom,&ui_menu_conf_from_eeprom +#define UI_MENU_EEPROM_CNT 2 +UI_MENU_ACTION2C(ui_menu_eeprom_saved,UI_ACTION_DUMMY,UI_TEXT_EEPROM_STORED); +UI_MENU_ACTION2C(ui_menu_eeprom_loaded,UI_ACTION_DUMMY,UI_TEXT_EEPROM_LOADED); +#else +#define UI_MENU_EEPROM_COND +#define UI_MENU_EEPROM_CNT 0 +#endif +#ifdef SOFTWARE_LEVELING +#define UI_MENU_SL_COND ,&ui_menu_conf_level +#define UI_MENU_SL_CNT 1 +UI_MENU_SUBMENU(ui_menu_conf_level, UI_TEXT_LEVEL, ui_menu_level); +#else +#define UI_MENU_SL_COND +#define UI_MENU_SL_CNT 0 +#endif +#ifdef STEP_COUNTER +#define UI_MENU_DELTA_COND ,&ui_menu_conf_delta +#define UI_MENU_DELTA_CNT 1 +UI_MENU_SUBMENU(ui_menu_conf_delta, UI_TEXT_DELTA, ui_menu_delta); +#else +#define UI_MENU_DELTA_COND +#define UI_MENU_DELTA_CNT 0 +#endif +#define UI_MENU_CONFIGURATION {UI_MENU_ADDCONDBACK &ui_menu_conf_general,UI_MENU_ADDCONDOPS &ui_menu_conf_accel,&ui_menu_conf_feed,&ui_menu_conf_extr UI_MENU_EEPROM_COND UI_MENU_DELTA_COND UI_MENU_SL_COND} +UI_MENU(ui_menu_configuration,UI_MENU_CONFIGURATION,UI_MENU_BACKCNT+USE_OPS+UI_MENU_EEPROM_CNT+UI_MENU_DELTA_CNT+UI_MENU_SL_CNT+4); +// Main menu +UI_MENU_SUBMENU(ui_menu_main1,UI_TEXT_QUICK_SETTINGS,ui_menu_quick); +UI_MENU_SUBMENU(ui_menu_main2, UI_TEXT_POSITION,ui_menu_positions); +UI_MENU_SUBMENU(ui_menu_main3,UI_TEXT_EXTRUDER,ui_menu_extruder); +UI_MENU_SUBMENU(ui_menu_main4,UI_TEXT_DEBUGGING,ui_menu_debugging); +UI_MENU_SUBMENU(ui_menu_main5,UI_TEXT_CONFIGURATION,ui_menu_configuration); +#define UI_MENU_MAIN {UI_MENU_ADDCONDBACK &ui_menu_main1,&ui_menu_main2,&ui_menu_main3,UI_MENU_FAN_COND UI_MENU_SD_COND &ui_menu_main4,&ui_menu_main5} +UI_MENU(ui_menu_main,UI_MENU_MAIN,5+UI_MENU_BACKCNT+UI_MENU_SD_CNT+UI_MENU_FAN_CNT); + +/* Define menus accessible by action commands + +You can create up to 10 user menus which are accessible by the action commands UI_ACTION_SHOW_USERMENU1 until UI_ACTION_SHOW_USERMENU10 +You this the same way as with the menus above or you use one of the above menus. Then add a define like + +#define UI_USERMENU1 ui_menu_conf_feed + +which assigns the menu stored in ui_menu_conf_feed to the action UI_ACTION_SHOW_USERMENU1. Make sure only to change the numbers and not the name of the define. + +When do you need this? You might want a fast button to change the temperature. In the default menu you have no menu +to change the temperature and view it the same time. So you need to make an action menu for this like: +UI_MENU_ACTION4C(ui_menu_extrtemp,UI_ACTION_EXTRUDER0_TEMP,"Temp. 0 :%E0\002C","","",""); +Then you assign this menu to a usermenu: +#define UI_USERMENU2 ui_menu_extrtemp + +Now you can assign the action UI_ACTION_SHOW_USERMENU2+UI_ACTION_TOPMENU to a key and that will now show the temperture screen and allows +the change of temperature with the next/previous buttons. + +*/ + +#endif // __UI_MENU_H