Luigi Santivetti | 69972f9 | 2019-11-12 22:55:40 +0000 | [diff] [blame^] | 1 | /* |
| 2 | settings.c - eeprom configuration handling |
| 3 | Part of Grbl |
| 4 | |
| 5 | Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC |
| 6 | Copyright (c) 2009-2011 Simen Svale Skogsrud |
| 7 | |
| 8 | Grbl is free software: you can redistribute it and/or modify |
| 9 | it under the terms of the GNU General Public License as published by |
| 10 | the Free Software Foundation, either version 3 of the License, or |
| 11 | (at your option) any later version. |
| 12 | |
| 13 | Grbl is distributed in the hope that it will be useful, |
| 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | GNU General Public License for more details. |
| 17 | |
| 18 | You should have received a copy of the GNU General Public License |
| 19 | along with Grbl. If not, see <http://www.gnu.org/licenses/>. |
| 20 | */ |
| 21 | |
| 22 | #include "grbl.h" |
| 23 | |
| 24 | settings_t settings; |
| 25 | |
| 26 | const __flash settings_t defaults = {\ |
| 27 | .pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS, |
| 28 | .stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME, |
| 29 | .step_invert_mask = DEFAULT_STEPPING_INVERT_MASK, |
| 30 | .dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK, |
| 31 | .status_report_mask = DEFAULT_STATUS_REPORT_MASK, |
| 32 | .junction_deviation = DEFAULT_JUNCTION_DEVIATION, |
| 33 | .arc_tolerance = DEFAULT_ARC_TOLERANCE, |
| 34 | .rpm_max = DEFAULT_SPINDLE_RPM_MAX, |
| 35 | .rpm_min = DEFAULT_SPINDLE_RPM_MIN, |
| 36 | .homing_dir_mask = DEFAULT_HOMING_DIR_MASK, |
| 37 | .homing_feed_rate = DEFAULT_HOMING_FEED_RATE, |
| 38 | .homing_seek_rate = DEFAULT_HOMING_SEEK_RATE, |
| 39 | .homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY, |
| 40 | .homing_pulloff = DEFAULT_HOMING_PULLOFF, |
| 41 | .flags = (DEFAULT_REPORT_INCHES << BIT_REPORT_INCHES) | \ |
| 42 | (DEFAULT_LASER_MODE << BIT_LASER_MODE) | \ |
| 43 | (DEFAULT_INVERT_ST_ENABLE << BIT_INVERT_ST_ENABLE) | \ |
| 44 | (DEFAULT_HARD_LIMIT_ENABLE << BIT_HARD_LIMIT_ENABLE) | \ |
| 45 | (DEFAULT_HOMING_ENABLE << BIT_HOMING_ENABLE) | \ |
| 46 | (DEFAULT_SOFT_LIMIT_ENABLE << BIT_SOFT_LIMIT_ENABLE) | \ |
| 47 | (DEFAULT_INVERT_LIMIT_PINS << BIT_INVERT_LIMIT_PINS) | \ |
| 48 | (DEFAULT_INVERT_PROBE_PIN << BIT_INVERT_PROBE_PIN), |
| 49 | .steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM, |
| 50 | .steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM, |
| 51 | .steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM, |
| 52 | .max_rate[X_AXIS] = DEFAULT_X_MAX_RATE, |
| 53 | .max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE, |
| 54 | .max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE, |
| 55 | .acceleration[X_AXIS] = DEFAULT_X_ACCELERATION, |
| 56 | .acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION, |
| 57 | .acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION, |
| 58 | .max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL), |
| 59 | .max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL), |
| 60 | .max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL)}; |
| 61 | |
| 62 | |
| 63 | // Method to store startup lines into EEPROM |
| 64 | void settings_store_startup_line(uint8_t n, char *line) |
| 65 | { |
| 66 | #ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE |
| 67 | protocol_buffer_synchronize(); // A startup line may contain a motion and be executing. |
| 68 | #endif |
| 69 | uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; |
| 70 | memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); |
| 71 | } |
| 72 | |
| 73 | |
| 74 | // Method to store build info into EEPROM |
| 75 | // NOTE: This function can only be called in IDLE state. |
| 76 | void settings_store_build_info(char *line) |
| 77 | { |
| 78 | // Build info can only be stored when state is IDLE. |
| 79 | memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE); |
| 80 | } |
| 81 | |
| 82 | |
| 83 | // Method to store coord data parameters into EEPROM |
| 84 | void settings_write_coord_data(uint8_t coord_select, float *coord_data) |
| 85 | { |
| 86 | #ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE |
| 87 | protocol_buffer_synchronize(); |
| 88 | #endif |
| 89 | uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; |
| 90 | memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); |
| 91 | } |
| 92 | |
| 93 | |
| 94 | // Method to store Grbl global settings struct and version number into EEPROM |
| 95 | // NOTE: This function can only be called in IDLE state. |
| 96 | void write_global_settings() |
| 97 | { |
| 98 | eeprom_put_char(0, SETTINGS_VERSION); |
| 99 | memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); |
| 100 | } |
| 101 | |
| 102 | |
| 103 | // Method to restore EEPROM-saved Grbl global settings back to defaults. |
| 104 | void settings_restore(uint8_t restore_flag) { |
| 105 | if (restore_flag & SETTINGS_RESTORE_DEFAULTS) { |
| 106 | settings = defaults; |
| 107 | write_global_settings(); |
| 108 | } |
| 109 | |
| 110 | if (restore_flag & SETTINGS_RESTORE_PARAMETERS) { |
| 111 | uint8_t idx; |
| 112 | float coord_data[N_AXIS]; |
| 113 | memset(&coord_data, 0, sizeof(coord_data)); |
| 114 | for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); } |
| 115 | } |
| 116 | |
| 117 | if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) { |
| 118 | #if N_STARTUP_LINE > 0 |
| 119 | eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0); |
| 120 | eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+1, 0); // Checksum |
| 121 | #endif |
| 122 | #if N_STARTUP_LINE > 1 |
| 123 | eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+1), 0); |
| 124 | eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+2), 0); // Checksum |
| 125 | #endif |
| 126 | } |
| 127 | |
| 128 | if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) { |
| 129 | eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); |
| 130 | eeprom_put_char(EEPROM_ADDR_BUILD_INFO+1 , 0); // Checksum |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | |
| 135 | // Reads startup line from EEPROM. Updated pointed line string data. |
| 136 | uint8_t settings_read_startup_line(uint8_t n, char *line) |
| 137 | { |
| 138 | uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; |
| 139 | if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { |
| 140 | // Reset line with default value |
| 141 | line[0] = 0; // Empty line |
| 142 | settings_store_startup_line(n, line); |
| 143 | return(false); |
| 144 | } |
| 145 | return(true); |
| 146 | } |
| 147 | |
| 148 | |
| 149 | // Reads startup line from EEPROM. Updated pointed line string data. |
| 150 | uint8_t settings_read_build_info(char *line) |
| 151 | { |
| 152 | if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) { |
| 153 | // Reset line with default value |
| 154 | line[0] = 0; // Empty line |
| 155 | settings_store_build_info(line); |
| 156 | return(false); |
| 157 | } |
| 158 | return(true); |
| 159 | } |
| 160 | |
| 161 | |
| 162 | // Read selected coordinate data from EEPROM. Updates pointed coord_data value. |
| 163 | uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) |
| 164 | { |
| 165 | uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; |
| 166 | if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { |
| 167 | // Reset with default zero vector |
| 168 | clear_vector_float(coord_data); |
| 169 | settings_write_coord_data(coord_select,coord_data); |
| 170 | return(false); |
| 171 | } |
| 172 | return(true); |
| 173 | } |
| 174 | |
| 175 | |
| 176 | // Reads Grbl global settings struct from EEPROM. |
| 177 | uint8_t read_global_settings() { |
| 178 | // Check version-byte of eeprom |
| 179 | uint8_t version = eeprom_get_char(0); |
| 180 | if (version == SETTINGS_VERSION) { |
| 181 | // Read settings-record and check checksum |
| 182 | if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { |
| 183 | return(false); |
| 184 | } |
| 185 | } else { |
| 186 | return(false); |
| 187 | } |
| 188 | return(true); |
| 189 | } |
| 190 | |
| 191 | |
| 192 | // A helper method to set settings from command line |
| 193 | uint8_t settings_store_global_setting(uint8_t parameter, float value) { |
| 194 | if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); } |
| 195 | if (parameter >= AXIS_SETTINGS_START_VAL) { |
| 196 | // Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines. |
| 197 | // NOTE: Ensure the setting index corresponds to the report.c settings printout. |
| 198 | parameter -= AXIS_SETTINGS_START_VAL; |
| 199 | uint8_t set_idx = 0; |
| 200 | while (set_idx < AXIS_N_SETTINGS) { |
| 201 | if (parameter < N_AXIS) { |
| 202 | // Valid axis setting found. |
| 203 | switch (set_idx) { |
| 204 | case 0: |
| 205 | #ifdef MAX_STEP_RATE_HZ |
| 206 | if (value*settings.max_rate[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); } |
| 207 | #endif |
| 208 | settings.steps_per_mm[parameter] = value; |
| 209 | break; |
| 210 | case 1: |
| 211 | #ifdef MAX_STEP_RATE_HZ |
| 212 | if (value*settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); } |
| 213 | #endif |
| 214 | settings.max_rate[parameter] = value; |
| 215 | break; |
| 216 | case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to mm/min^2 for grbl internal use. |
| 217 | case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use. |
| 218 | } |
| 219 | break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call. |
| 220 | } else { |
| 221 | set_idx++; |
| 222 | // If axis index greater than N_AXIS or setting index greater than number of axis settings, error out. |
| 223 | if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) { return(STATUS_INVALID_STATEMENT); } |
| 224 | parameter -= AXIS_SETTINGS_INCREMENT; |
| 225 | } |
| 226 | } |
| 227 | } else { |
| 228 | // Store non-axis Grbl settings |
| 229 | uint8_t int_value = trunc(value); |
| 230 | switch(parameter) { |
| 231 | case 0: |
| 232 | if (int_value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } |
| 233 | settings.pulse_microseconds = int_value; break; |
| 234 | case 1: settings.stepper_idle_lock_time = int_value; break; |
| 235 | case 2: |
| 236 | settings.step_invert_mask = int_value; |
| 237 | st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. |
| 238 | break; |
| 239 | case 3: |
| 240 | settings.dir_invert_mask = int_value; |
| 241 | st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. |
| 242 | break; |
| 243 | case 4: // Reset to ensure change. Immediate re-init may cause problems. |
| 244 | if (int_value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } |
| 245 | else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } |
| 246 | break; |
| 247 | case 5: // Reset to ensure change. Immediate re-init may cause problems. |
| 248 | if (int_value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } |
| 249 | else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; } |
| 250 | break; |
| 251 | case 6: // Reset to ensure change. Immediate re-init may cause problems. |
| 252 | if (int_value) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } |
| 253 | else { settings.flags &= ~BITFLAG_INVERT_PROBE_PIN; } |
| 254 | probe_configure_invert_mask(false); |
| 255 | break; |
| 256 | case 10: settings.status_report_mask = int_value; break; |
| 257 | case 11: settings.junction_deviation = value; break; |
| 258 | case 12: settings.arc_tolerance = value; break; |
| 259 | case 13: |
| 260 | if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; } |
| 261 | else { settings.flags &= ~BITFLAG_REPORT_INCHES; } |
| 262 | system_flag_wco_change(); // Make sure WCO is immediately updated. |
| 263 | break; |
| 264 | case 20: |
| 265 | if (int_value) { |
| 266 | if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); } |
| 267 | settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; |
| 268 | } else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; } |
| 269 | break; |
| 270 | case 21: |
| 271 | if (int_value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } |
| 272 | else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } |
| 273 | limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later. |
| 274 | break; |
| 275 | case 22: |
| 276 | if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; } |
| 277 | else { |
| 278 | settings.flags &= ~BITFLAG_HOMING_ENABLE; |
| 279 | settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits. |
| 280 | } |
| 281 | break; |
| 282 | case 23: settings.homing_dir_mask = int_value; break; |
| 283 | case 24: settings.homing_feed_rate = value; break; |
| 284 | case 25: settings.homing_seek_rate = value; break; |
| 285 | case 26: settings.homing_debounce_delay = int_value; break; |
| 286 | case 27: settings.homing_pulloff = value; break; |
| 287 | case 30: settings.rpm_max = value; spindle_init(); break; // Re-initialize spindle rpm calibration |
| 288 | case 31: settings.rpm_min = value; spindle_init(); break; // Re-initialize spindle rpm calibration |
| 289 | case 32: |
| 290 | #ifdef VARIABLE_SPINDLE |
| 291 | if (int_value) { settings.flags |= BITFLAG_LASER_MODE; } |
| 292 | else { settings.flags &= ~BITFLAG_LASER_MODE; } |
| 293 | #else |
| 294 | return(STATUS_SETTING_DISABLED_LASER); |
| 295 | #endif |
| 296 | break; |
| 297 | default: |
| 298 | return(STATUS_INVALID_STATEMENT); |
| 299 | } |
| 300 | } |
| 301 | write_global_settings(); |
| 302 | return(STATUS_OK); |
| 303 | } |
| 304 | |
| 305 | |
| 306 | // Initialize the config subsystem |
| 307 | void settings_init() { |
| 308 | if(!read_global_settings()) { |
| 309 | report_status_message(STATUS_SETTING_READ_FAIL); |
| 310 | settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data. |
| 311 | report_grbl_settings(); |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | |
| 316 | // Returns step pin mask according to Grbl internal axis indexing. |
| 317 | uint8_t get_step_pin_mask(uint8_t axis_idx) |
| 318 | { |
| 319 | if ( axis_idx == X_AXIS ) { return((1<<X_STEP_BIT)); } |
| 320 | if ( axis_idx == Y_AXIS ) { return((1<<Y_STEP_BIT)); } |
| 321 | return((1<<Z_STEP_BIT)); |
| 322 | } |
| 323 | |
| 324 | |
| 325 | // Returns direction pin mask according to Grbl internal axis indexing. |
| 326 | uint8_t get_direction_pin_mask(uint8_t axis_idx) |
| 327 | { |
| 328 | if ( axis_idx == X_AXIS ) { return((1<<X_DIRECTION_BIT)); } |
| 329 | if ( axis_idx == Y_AXIS ) { return((1<<Y_DIRECTION_BIT)); } |
| 330 | return((1<<Z_DIRECTION_BIT)); |
| 331 | } |
| 332 | |
| 333 | |
| 334 | // Returns limit pin mask according to Grbl internal axis indexing. |
| 335 | uint8_t get_limit_pin_mask(uint8_t axis_idx) |
| 336 | { |
| 337 | if ( axis_idx == X_AXIS ) { return((1<<X_LIMIT_BIT)); } |
| 338 | if ( axis_idx == Y_AXIS ) { return((1<<Y_LIMIT_BIT)); } |
| 339 | return((1<<Z_LIMIT_BIT)); |
| 340 | } |