add og firmware

This commit is contained in:
Siwat Sirichai 2022-01-29 17:28:20 +07:00
parent f3c8b13122
commit b2495bdfac
186 changed files with 119377 additions and 1 deletions

54
.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
.settings
.project
.cproject
Debug
Firmware/Configuration_prusa.h
Firmware/Doc
/Firmware/.vs/Firmware/v14
/Firmware/__vm
/Firmware/Firmware.sln
/Firmware/Firmware.vcxproj
/Firmware/Firmware.vcxproj.filters
/Firmware/Firmware - Shortcut.lnk
/Firmware/variants/1_75mm_MK3-MMU-EINSy10a-E3Dv6full.h.bak
/Firmware/Marlin_main.cpp~RF12cfae7.TMP
/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h.bak
/html
/latex
/Doxyfile
/Firmware/builds/1_75mm_MK3-EINY04-E3Dv6full
/Firmware/Configuration_prusa.h.bak
/Firmware/Configuration_prusa_backup.h
/Firmware/ultralcd_implementation_hitachi_HD44780.h.bak
/Firmware/ultralcd.cpp.bak
/Firmware/temperature.cpp.bak
/Firmware/pins.h.bak
/Firmware/Marlin_main.cpp.bak
/Firmware/language_pl.h.bak
/Firmware/language_it.h.bak
/Firmware/language_es.h.bak
/Firmware/language_en.h.bak
/Firmware/language_de.h.bak
/Firmware/language_cz.h.bak
/Firmware/variants/1_75mm_MK2-MultiMaterial-RAMBo13a-E3Dv6full.h
/Firmware/variants/1_75mm_MK2-MultiMaterial-RAMBo10a-E3Dv6full.h
/Firmware/variants/1_75mm_MK2-EINY01-E3Dv6full.h.bak
/Firmware/variants/1_75mm_MK1-RAMBo13a-E3Dv6full.h
/Firmware/variants/1_75mm_MK1-RAMBo10a-E3Dv6full.h
/lang/*.bin
/lang/*.hex
/lang/*.dat
/lang/*.tmp
/lang/*.out
/lang/not_tran.txt
/lang/not_used.txt
/lang/progmem1.chr
/lang/progmem1.lss
/lang/progmem1.txt
/lang/progmem1.var
/lang/text.sym
/lang/textaddr.txt
/build-env/
/Firmware/Firmware.vcxproj
/Firmware/Configuration_prusa_bckp.h
/Firmware/variants/printers.h

35
.travis.yml Normal file
View File

@ -0,0 +1,35 @@
dist: trusty
before_install:
- sudo apt-get install -y ninja-build
# Arduino IDE adds a lot of noise caused by network traffic, trying to firewall it off
- sudo iptables -P INPUT DROP
- sudo iptables -P FORWARD DROP
- sudo iptables -P OUTPUT ACCEPT
- sudo iptables -A INPUT -i lo -j ACCEPT
- sudo iptables -A OUTPUT -o lo -j ACCEPT
- sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
script:
- bash -x test.sh
- cp Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK3S-EINSy10a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK3-EINSy10a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK25S-RAMBo13a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK25S-RAMBo10a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK25-RAMBo13a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK25-RAMBo10a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK2-RAMBo13a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK2-RAMBo10a-E3Dv6full variant failed" && false; }

24
CMakeLists.txt Normal file
View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.1)
set (CMAKE_CXX_STANDARD 11)
project(cmake_test)
# Prepare "Catch" library for other executables
set(CATCH_INCLUDE_DIR Catch2)
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
# Make test executable
set(TEST_SOURCES
Tests/tests.cpp
Tests/Example_test.cpp
Tests/Timer_test.cpp
Tests/AutoDeplete_test.cpp
Tests/PrusaStatistics_test.cpp
Firmware/Timer.cpp
Firmware/AutoDeplete.cpp
)
add_executable(tests ${TEST_SOURCES})
target_include_directories(tests PRIVATE Tests)
target_link_libraries(tests Catch)

13287
Catch2/catch.hpp Normal file

File diff suppressed because it is too large Load Diff

79
Firmware/AutoDeplete.cpp Normal file
View File

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

17
Firmware/AutoDeplete.h Normal file
View File

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

29
Firmware/BlinkM.cpp Normal file
View File

@ -0,0 +1,29 @@
/*
BlinkM.cpp - Library for controlling a BlinkM over i2c
Created by Tim Koster, August 21 2013.
*/
#include "Marlin.h"
#ifdef BLINKM
#if (ARDUINO >= 100)
# include "Arduino.h"
#else
# include "WProgram.h"
#endif
#include "BlinkM.h"
void SendColors(byte red, byte grn, byte blu)
{
Wire.begin();
Wire.beginTransmission(0x09);
Wire.write('o'); //to disable ongoing script, only needs to be used once
Wire.write('n');
Wire.write(red);
Wire.write(grn);
Wire.write(blu);
Wire.endTransmission();
}
#endif //BLINKM

14
Firmware/BlinkM.h Normal file
View File

@ -0,0 +1,14 @@
/*
BlinkM.h
Library header file for BlinkM library
*/
#if (ARDUINO >= 100)
# include "Arduino.h"
#else
# include "WProgram.h"
#endif
#include "Wire.h"
void SendColors(byte red, byte grn, byte blu);

View File

@ -0,0 +1,10 @@
#include "Configuration.h"
#include "Configuration_prusa.h"
const uint16_t _nPrinterType PROGMEM=PRINTER_TYPE;
const char _sPrinterName[] PROGMEM=PRINTER_NAME;
const uint16_t _nPrinterMmuType PROGMEM=PRINTER_MMU_TYPE;
const char _sPrinterMmuName[] PROGMEM=PRINTER_MMU_NAME;
uint16_t nPrinterType;
PGM_P sPrinterName;

556
Firmware/Configuration.h Normal file
View File

@ -0,0 +1,556 @@
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include "boards.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
//-//
#include <avr/pgmspace.h>
extern const uint16_t _nPrinterType;
extern const char _sPrinterName[] PROGMEM;
extern const uint16_t _nPrinterMmuType;
extern const char _sPrinterMmuName[] PROGMEM;
extern uint16_t nPrinterType;
extern PGM_P sPrinterName;
// Firmware version
#define FW_VERSION "3.8.1"
#define FW_COMMIT_NR 2869
// FW_VERSION_UNKNOWN means this is an unofficial build.
// The firmware should only be checked into github with this symbol.
#define FW_DEV_VERSION FW_VERSION_UNKNOWN
#define FW_REPOSITORY "Unknown"
#define FW_VERSION_FULL FW_VERSION "-" STR(FW_COMMIT_NR)
// G-code language level
#define GCODE_LEVEL 1
// Debug version has debugging enabled (the symbol DEBUG_BUILD is set).
// The debug build may be a bit slower than the non-debug build, therefore the debug build should
// not be shipped to a customer.
#define FW_VERSION_DEBUG 6
// This is a development build. A development build is either built from an unofficial git repository,
// or from an unofficial branch, or it does not have a label set. Only the build server should set this build type.
#define FW_VERSION_DEVEL 5
// This is an alpha release. Only the build server should set this build type.
#define FW_VERSION_ALPHA 4
// This is a beta release. Only the build server should set this build type.
#define FW_VERSION_BETA 3
// This is a release candidate build. Only the build server should set this build type.
#define FW_VERSION_RC 2
// This is a final release. Only the build server should set this build type.
#define FW_VERSION_GOLD 1
// This is an unofficial build. The firmware should only be checked into github with this symbol,
// the build server shall never produce builds with this build type.
#define FW_VERSION_UNKNOWN 0
#if FW_DEV_VERSION == FW_VERSION_DEBUG
#define DEBUG_BUILD
#else
#undef DEBUG_BUILD
#endif
#include "Configuration_prusa.h"
#define FW_PRUSA3D_MAGIC "PRUSA3DFW"
#define FW_PRUSA3D_MAGIC_LEN 10
#include "eeprom.h"
// This configuration file contains the basic settings.
// Advanced settings can be found in Configuration_adv.h
// BASIC SETTINGS: select your board type, temperature sensor type, axis scaling, and endstop configuration
// User-specified version info of this build to display in [Pronterface, etc] terminal window during
// startup. Implementation of an idea by Prof Braino to inform user that any changes made to this
// build by the user have been successfully uploaded into firmware.
//#define STRING_VERSION "1.0.2"
#define STRING_VERSION_CONFIG_H __DATE__ " " __TIME__ // build date and time
#define STRING_CONFIG_H_AUTHOR "(none, default config)" // Who made the changes.
// SERIAL_PORT selects which serial port should be used for communication with the host.
// This allows the connection of wireless adapters (for instance) to non-default port pins.
// Serial port 0 is still used by the Arduino bootloader regardless of this setting.
#define SERIAL_PORT 0
// This determines the communication speed of the printer
#define BAUDRATE 115200
// This enables the serial port associated to the Bluetooth interface
//#define BTENABLED // Enable BT interface on AT90USB devices
// The following define selects which electronics board you have.
// Please choose the name from boards.h that matches your setup
// Define this to set a unique identifier for this printer, (Used by some programs to differentiate between machines)
// You can use an online service to generate a random UUID. (eg http://www.uuidgenerator.net/version4)
// #define MACHINE_UUID "00000000-0000-0000-0000-000000000000"
// This defines the number of extruders
#define EXTRUDERS 1
//// The following define selects which power supply you have. Please choose the one that matches your setup
// 1 = ATX
// 2 = X-Box 360 203Watts (the blue wire connected to PS_ON and the red wire to VCC)
#define POWER_SUPPLY 1
// Define this to have the electronics keep the power supply off on startup. If you don't know what this is leave it.
// #define PS_DEFAULT_OFF
// Actual temperature must be close to target for this long before M109 returns success
#define TEMP_RESIDENCY_TIME 3 // (seconds)
#define TEMP_HYSTERESIS 5 // (degC) range of +/- temperatures considered "close" to the target one
#define TEMP_WINDOW 1 // (degC) Window around target to start the residency timer x degC early.
// If your bed has low resistance e.g. .6 ohm and throws the fuse you can duty cycle it to reduce the
// average current. The value should be an integer and the heat bed will be turned on for 1 interval of
// HEATER_BED_DUTY_CYCLE_DIVIDER intervals.
//#define HEATER_BED_DUTY_CYCLE_DIVIDER 4
// If you want the M105 heater power reported in watts, define the BED_WATTS, and (shared for all extruders) EXTRUDER_WATTS
//#define EXTRUDER_WATTS (12.0*12.0/6.7) // P=I^2/R
//#define BED_WATTS (12.0*12.0/1.1) // P=I^2/R
// PID settings:
// Comment the following line to disable PID and enable bang-bang.
#define PIDTEMP
#define BANG_MAX 255 // limits current to nozzle while in bang-bang mode; 255=full current
#define PID_MAX BANG_MAX // limits current to nozzle while PID is active; 255=full current
#ifdef PIDTEMP
//#define PID_DEBUG // Sends debug data to the serial port.
//#define PID_OPENLOOP 1 // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX
//#define SLOW_PWM_HEATERS // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay
#define PID_INTEGRAL_DRIVE_MAX PID_MAX //limit for the integral term
#define PID_K1 0.95 //smoothing factor within the PID
#define PID_dT ((OVERSAMPLENR * 10.0)/(F_CPU / 64.0 / 256.0)) //sampling period of the temperature routine
// If you are using a pre-configured hotend then you can use one of the value sets by uncommenting it
// Ultimaker
// MakerGear
// #define DEFAULT_Kp 7.0
// #define DEFAULT_Ki 0.1
// #define DEFAULT_Kd 12
// Mendel Parts V9 on 12V
// #define DEFAULT_Kp 63.0
// #define DEFAULT_Ki 2.25
// #define DEFAULT_Kd 440
#endif // PIDTEMP
//this prevents dangerous Extruder moves, i.e. if the temperature is under the limit
//can be software-disabled for whatever purposes by
#define PREVENT_DANGEROUS_EXTRUDE
//if PREVENT_DANGEROUS_EXTRUDE is on, you can still disable (uncomment) very long bits of extrusion separately.
#define PREVENT_LENGTHY_EXTRUDE
#ifdef DEBUG_DISABLE_PREVENT_EXTRUDER
#undef PREVENT_DANGEROUS_EXTRUDE
#undef PREVENT_LENGTHY_EXTRUDE
#endif //DEBUG_DISABLE_PREVENT_EXTRUDER
#define EXTRUDE_MAXLENGTH (X_MAX_LENGTH+Y_MAX_LENGTH) //prevent extrusion of very large distances.
/*================== Thermal Runaway Protection ==============================
This is a feature to protect your printer from burn up in flames if it has
a thermistor coming off place (this happened to a friend of mine recently and
motivated me writing this feature).
The issue: If a thermistor come off, it will read a lower temperature than actual.
The system will turn the heater on forever, burning up the filament and anything
else around.
After the temperature reaches the target for the first time, this feature will
start measuring for how long the current temperature stays below the target
minus _HYSTERESIS (set_temperature - THERMAL_RUNAWAY_PROTECTION_HYSTERESIS).
If it stays longer than _PERIOD, it means the thermistor temperature
cannot catch up with the target, so something *may be* wrong. Then, to be on the
safe side, the system will he halt.
Bear in mind the count down will just start AFTER the first time the
thermistor temperature is over the target, so you will have no problem if
your extruder heater takes 2 minutes to hit the target on heating.
*/
// If you want to enable this feature for all your extruder heaters,
// uncomment the 2 defines below:
// Parameters for all extruder heaters
//#define THERMAL_RUNAWAY_PROTECTION_PERIOD 40 //in seconds
//#define THERMAL_RUNAWAY_PROTECTION_HYSTERESIS 4 // in degree Celsius
// If you want to enable this feature for your bed heater,
// uncomment the 2 defines below:
// Parameters for the bed heater
//#define THERMAL_RUNAWAY_PROTECTION_BED_PERIOD 20 //in seconds
//#define THERMAL_RUNAWAY_PROTECTION_BED_HYSTERESIS 2 // in degree Celsius
//===========================================================================
//===========================================================================
//=============================Mechanical Settings===========================
//===========================================================================
// Uncomment the following line to enable CoreXY kinematics
// #define COREXY
// coarse Endstop Settings
#define ENDSTOPPULLUPS // Comment this out (using // at the start of the line) to disable the endstop pullup resistors
#ifndef ENDSTOPPULLUPS
// fine endstop settings: Individual pullups. will be ignored if ENDSTOPPULLUPS is defined
// #define ENDSTOPPULLUP_XMAX
// #define ENDSTOPPULLUP_YMAX
// #define ENDSTOPPULLUP_ZMAX
// #define ENDSTOPPULLUP_XMIN
// #define ENDSTOPPULLUP_YMIN
// #define ENDSTOPPULLUP_ZMIN
#endif
#ifdef ENDSTOPPULLUPS
#define ENDSTOPPULLUP_XMAX
#define ENDSTOPPULLUP_YMAX
#define ENDSTOPPULLUP_ZMAX
#define ENDSTOPPULLUP_XMIN
#define ENDSTOPPULLUP_YMIN
#define ENDSTOPPULLUP_ZMIN
#endif
// The pullups are needed if you directly connect a mechanical endswitch between the signal and ground pins.
#define X_MAX_ENDSTOP_INVERTING 0 // set to 1 to invert the logic of the endstop.
#define Y_MAX_ENDSTOP_INVERTING 0 // set to 1 to invert the logic of the endstop.
#define Z_MAX_ENDSTOP_INVERTING 1 // set to 1 to invert the logic of the endstop.
//#define DISABLE_MAX_ENDSTOPS
//#define DISABLE_MIN_ENDSTOPS
// Disable max endstops for compatibility with endstop checking routine
#if defined(COREXY) && !defined(DISABLE_MAX_ENDSTOPS)
#define DISABLE_MAX_ENDSTOPS
#endif
// 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
#define E_ENABLE_ON 0 // For all extruders
// Disables axis when it's not being used.
#define DISABLE_X 0
#define DISABLE_Y 0
#define DISABLE_Z 0
#define DISABLE_E 0// For all extruders
#define DISABLE_INACTIVE_EXTRUDER 1 //disable only inactive extruders and keep active extruder enabled
// 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
#ifdef DEBUG_DISABLE_SWLIMITS
#define min_software_endstops 0
#define max_software_endstops 0
#else
#define min_software_endstops 1 // If true, axis won't move to coordinates less than HOME_POS.
#define max_software_endstops 1 // If true, axis won't move to coordinates greater than the defined lengths below.
#endif //DEBUG_DISABLE_SWLIMITS
#define X_MAX_LENGTH (X_MAX_POS - X_MIN_POS)
#define Y_MAX_LENGTH (Y_MAX_POS - Y_MIN_POS)
#define Z_MAX_LENGTH (Z_MAX_POS - Z_MIN_POS)
#define Z_HEIGHT_HIDE_LIVE_ADJUST_MENU 2.0f
#define HOME_Z_SEARCH_THRESHOLD 0.15f // Threshold of the Z height in calibration
//============================= Bed Auto Leveling ===========================
//#define ENABLE_AUTO_BED_LEVELING // Delete the comment to enable (remove // at the start of the line)
#define Z_PROBE_REPEATABILITY_TEST // If not commented out, Z-Probe Repeatability test will be included if Auto Bed Leveling is Enabled.
#ifdef ENABLE_AUTO_BED_LEVELING
// There are 2 different ways to pick the X and Y locations to probe:
// - "grid" mode
// Probe every point in a rectangular grid
// You must specify the rectangle, and the density of sample points
// This mode is preferred because there are more measurements.
// It used to be called ACCURATE_BED_LEVELING but "grid" is more descriptive
// - "3-point" mode
// Probe 3 arbitrary points on the bed (that aren't colinear)
// You must specify the X & Y coordinates of all 3 points
#define AUTO_BED_LEVELING_GRID
// with AUTO_BED_LEVELING_GRID, the bed is sampled in a
// AUTO_BED_LEVELING_GRID_POINTSxAUTO_BED_LEVELING_GRID_POINTS grid
// and least squares solution is calculated
// Note: this feature occupies 10'206 byte
#ifdef AUTO_BED_LEVELING_GRID
// set the rectangle in which to probe
#define LEFT_PROBE_BED_POSITION 15
#define RIGHT_PROBE_BED_POSITION 170
#define BACK_PROBE_BED_POSITION 180
#define FRONT_PROBE_BED_POSITION 20
// set the number of grid points per dimension
// I wouldn't see a reason to go above 3 (=9 probing points on the bed)
#define AUTO_BED_LEVELING_GRID_POINTS 2
#else // not AUTO_BED_LEVELING_GRID
// with no grid, just probe 3 arbitrary points. A simple cross-product
// is used to esimate the plane of the print bed
#define ABL_PROBE_PT_1_X 15
#define ABL_PROBE_PT_1_Y 180
#define ABL_PROBE_PT_2_X 15
#define ABL_PROBE_PT_2_Y 20
#define ABL_PROBE_PT_3_X 170
#define ABL_PROBE_PT_3_Y 20
#endif // AUTO_BED_LEVELING_GRID
// these are the offsets to the probe relative to the extruder tip (Hotend - Probe)
// X and Y offsets must be integers
#define X_PROBE_OFFSET_FROM_EXTRUDER -25
#define Y_PROBE_OFFSET_FROM_EXTRUDER -29
#define Z_PROBE_OFFSET_FROM_EXTRUDER -12.35
#define Z_RAISE_BEFORE_HOMING 4 // (in mm) Raise Z before homing (G28) for Probe Clearance.
// Be sure you have this distance over your Z_MAX_POS in case
#define XY_TRAVEL_SPEED 8000 // X and Y axis travel speed between probes, in mm/min
#define Z_RAISE_BEFORE_PROBING 15 //How much the extruder will be raised before traveling to the first probing point.
#define Z_RAISE_BETWEEN_PROBINGS 5 //How much the extruder will be raised when traveling from between next probing points
//#define Z_PROBE_SLED // turn on if you have a z-probe mounted on a sled like those designed by Charles Bell
//#define SLED_DOCKING_OFFSET 5 // the extra distance the X axis must travel to pickup the sled. 0 should be fine but you can push it further if you'd like.
//If defined, the Probe servo will be turned on only during movement and then turned off to avoid jerk
//The value is the delay to turn the servo off after powered on - depends on the servo speed; 300ms is good value, but you can try lower it.
// You MUST HAVE the SERVO_ENDSTOPS defined to use here a value higher than zero otherwise your code will not compile.
// #define PROBE_SERVO_DEACTIVATION_DELAY 300
//If you have enabled the Bed Auto Leveling and are using the same Z Probe for Z Homing,
//it is highly recommended you let this Z_SAFE_HOMING enabled!
//#define Z_SAFE_HOMING // This feature is meant to avoid Z homing with probe outside the bed area.
// When defined, it will:
// - Allow Z homing only after X and Y homing AND stepper drivers still enabled
// - If stepper drivers timeout, it will need X and Y homing again before Z homing
// - Position the probe in a defined XY point before Z Homing when homing all axis (G28)
// - Block Z homing only when the probe is outside bed area.
#ifdef Z_SAFE_HOMING
#define Z_SAFE_HOMING_X_POINT (X_MAX_LENGTH/2) // X point for Z homing when homing all axis (G28)
#define Z_SAFE_HOMING_Y_POINT (Y_MAX_LENGTH/2) // Y point for Z homing when homing all axis (G28)
#endif
#ifdef AUTO_BED_LEVELING_GRID // Check if Probe_Offset * Grid Points is greater than Probing Range
#if X_PROBE_OFFSET_FROM_EXTRUDER < 0
#if (-(X_PROBE_OFFSET_FROM_EXTRUDER * AUTO_BED_LEVELING_GRID_POINTS) >= (RIGHT_PROBE_BED_POSITION - LEFT_PROBE_BED_POSITION))
#error "The X axis probing range is not enough to fit all the points defined in AUTO_BED_LEVELING_GRID_POINTS"
#endif
#else
#if ((X_PROBE_OFFSET_FROM_EXTRUDER * AUTO_BED_LEVELING_GRID_POINTS) >= (RIGHT_PROBE_BED_POSITION - LEFT_PROBE_BED_POSITION))
#error "The X axis probing range is not enough to fit all the points defined in AUTO_BED_LEVELING_GRID_POINTS"
#endif
#endif
#if Y_PROBE_OFFSET_FROM_EXTRUDER < 0
#if (-(Y_PROBE_OFFSET_FROM_EXTRUDER * AUTO_BED_LEVELING_GRID_POINTS) >= (BACK_PROBE_BED_POSITION - FRONT_PROBE_BED_POSITION))
#error "The Y axis probing range is not enough to fit all the points defined in AUTO_BED_LEVELING_GRID_POINTS"
#endif
#else
#if ((Y_PROBE_OFFSET_FROM_EXTRUDER * AUTO_BED_LEVELING_GRID_POINTS) >= (BACK_PROBE_BED_POSITION - FRONT_PROBE_BED_POSITION))
#error "The Y axis probing range is not enough to fit all the points defined in AUTO_BED_LEVELING_GRID_POINTS"
#endif
#endif
#endif
#endif // ENABLE_AUTO_BED_LEVELING
// The position of the homing switches
//#define MANUAL_HOME_POSITIONS // If defined, MANUAL_*_HOME_POS below will be used
//#define BED_CENTER_AT_0_0 // If defined, the center of the bed is at (X=0, Y=0)
//Manual homing switch locations:
// For deltabots this means top and center of the Cartesian print volume.
// Offset of the extruders (uncomment if using more than one and relying on firmware to position when changing).
// The offset has to be X=0, Y=0 for the extruder 0 hotend (default extruder).
// For the other hotends it is their distance from the extruder 0 hotend.
// #define EXTRUDER_OFFSET_X {0.0, 20.00} // (in mm) for each extruder, offset of the hotend on the X axis
// #define EXTRUDER_OFFSET_Y {0.0, 5.00} // (in mm) for each extruder, offset of the hotend on the Y axis
// The speed change that does not require acceleration (i.e. the software might assume it can be done instantaneously)
#define DEFAULT_XJERK 10 // (mm/sec)
#define DEFAULT_YJERK 10 // (mm/sec)
#define DEFAULT_ZJERK 0.4 // (mm/sec)
#define DEFAULT_EJERK 2.5 // (mm/sec)
//===========================================================================
//=============================Additional Features===========================
//===========================================================================
// Custom M code points
#define CUSTOM_M_CODES
#ifdef CUSTOM_M_CODES
#define CUSTOM_M_CODE_SET_Z_PROBE_OFFSET 851
#define Z_PROBE_OFFSET_RANGE_MIN -15
#define Z_PROBE_OFFSET_RANGE_MAX -5
#endif
// EEPROM
// The microcontroller can store settings in the EEPROM, e.g. max velocity...
// M500 - stores parameters in EEPROM
// M501 - reads parameters from EEPROM (if you need reset them after you changed them temporarily).
// M502 - reverts to the default "factory settings". You still need to store them in EEPROM afterwards if you want to.
//define this to enable EEPROM support
//#define EEPROM_SETTINGS
//to disable EEPROM Serial responses and decrease program space by ~1700 byte: comment this out:
// please keep turned on if you can.
//#define EEPROM_CHITCHAT
// Host Keepalive
//
// When enabled Marlin will send a busy status message to the host
// every couple of seconds when it can't accept commands.
//
#ifndef HEATBED_ANALYSIS
#define HOST_KEEPALIVE_FEATURE // Disable this if your host doesn't like keepalive messages
#endif //HEATBED_ANALYSIS
#define HOST_KEEPALIVE_INTERVAL 2 // Number of seconds between "busy" messages. Set with M113.
//LCD and SD support
#define SDSUPPORT // Enable SD Card Support in Hardware Console
//#define SDSLOW // Use slower SD transfer mode (not normally needed - uncomment if you're getting volume init error)
#define SD_CHECK_AND_RETRY // Use CRC checks and retries on the SD communication
#define ENCODER_PULSES_PER_STEP 4 // Increase if you have a high resolution encoder
//#define ENCODER_STEPS_PER_MENU_ITEM 1 // Set according to ENCODER_PULSES_PER_STEP or your liking
// The RepRapDiscount Smart Controller (white PCB)
// http://reprap.org/wiki/RepRapDiscount_Smart_Controller
#define REPRAP_DISCOUNT_SMART_CONTROLLER
#define SDSUPPORT
#define LCD_WIDTH 20
#define LCD_HEIGHT 4
// Increase the FAN pwm frequency. Removes the PWM noise but increases heating in the FET/Arduino
//#define FAST_PWM_FAN
// Temperature status LEDs that display the hotend and bet temperature.
// If all hotends and bed temperature and temperature setpoint are < 54C then the BLUE led is on.
// Otherwise the RED led is on. There is 1C hysteresis.
//#define TEMP_STAT_LEDS
// Use software PWM to drive the fan, as for the heaters. This uses a very low frequency
// which is not ass annoying as with the hardware PWM. On the other hand, if this frequency
// is too low, you should also increment SOFT_PWM_SCALE.
#define FAN_SOFT_PWM
#define FAN_SOFT_PWM_BITS 4 //PWM bit resolution = 4bits, freq = 62.5Hz
// Bed soft pwm
#define HEATER_BED_SOFT_PWM_BITS 5 //PWM bit resolution = 5bits, freq = 31.25Hz
// Incrementing this by 1 will double the software PWM frequency,
// affecting heaters, and the fan if FAN_SOFT_PWM is enabled.
// However, control resolution will be halved for each increment;
// at zero value, there are 128 effective control positions.
#define SOFT_PWM_SCALE 0
// M240 Triggers a camera by emulating a Canon RC-1 Remote
// Data from: http://www.doc-diy.net/photo/rc-1_hacked/
// #define PHOTOGRAPH_PIN 23
// SF send wrong arc g-codes when using Arc Point as fillet procedure
//#define SF_ARC_FIX
//define BlinkM/CyzRgb Support
//#define BLINKM
/*********************************************************************\
* R/C SERVO support
* Sponsored by TrinityLabs, Reworked by codexmas
**********************************************************************/
// Number of servos
//
// If you select a configuration below, this will receive a default value and does not need to be set manually
// set it manually if you have more servos than extruders and wish to manually control some
// leaving it undefined or defining as 0 will disable the servo subsystem
// If unsure, leave commented / disabled
//
//#define NUM_SERVOS 3 // Servo index starts with 0 for M280 command
#define DEFAULT_NOMINAL_FILAMENT_DIA 1.75 //Enter the diameter (in mm) of the filament generally used (3.0 mm or 1.75 mm). Used by the volumetric extrusion.
// Calibration status of the machine, to be stored into the EEPROM,
// (unsigned char*)EEPROM_CALIBRATION_STATUS
enum CalibrationStatus
{
// Freshly assembled, needs to peform a self-test and the XYZ calibration.
CALIBRATION_STATUS_ASSEMBLED = 255,
// For the wizard: self test has been performed, now the XYZ calibration is needed.
CALIBRATION_STATUS_XYZ_CALIBRATION = 250,
// For the wizard: factory assembled, needs to run Z calibration.
CALIBRATION_STATUS_Z_CALIBRATION = 240,
// The XYZ calibration has been performed, now it remains to run the V2Calibration.gcode.
CALIBRATION_STATUS_LIVE_ADJUST = 230,
// Calibrated, ready to print.
CALIBRATION_STATUS_CALIBRATED = 1,
// Legacy: resetted by issuing a G86 G-code.
// This value can only be expected after an upgrade from the initial MK2 firmware releases.
// Currently the G86 sets the calibration status to
CALIBRATION_STATUS_UNKNOWN = 0,
};
#include "Configuration_adv.h"
#include "thermistortables.h"
#endif //__CONFIGURATION_H

View File

@ -0,0 +1,341 @@
//! @file
#include "Marlin.h"
#include "planner.h"
#include "temperature.h"
#include "ultralcd.h"
#include "ConfigurationStore.h"
#include "Configuration_prusa.h"
#ifdef MESH_BED_LEVELING
#include "mesh_bed_leveling.h"
#endif
#ifdef TMC2130
#include "tmc2130.h"
#endif
M500_conf cs;
//! @brief Write data to EEPROM
//! @param pos destination in EEPROM, 0 is start
//! @param value value to be written
//! @param size size of type pointed by value
//! @param name name of variable written, used only for debug input if DEBUG_EEPROM_WRITE defined
//! @retval true success
//! @retval false failed
#ifdef DEBUG_EEPROM_WRITE
static bool EEPROM_writeData(uint8_t* pos, uint8_t* value, uint8_t size, const char* name)
#else //DEBUG_EEPROM_WRITE
static bool EEPROM_writeData(uint8_t* pos, uint8_t* value, uint8_t size, const char*)
#endif //DEBUG_EEPROM_WRITE
{
#ifdef DEBUG_EEPROM_WRITE
printf_P(PSTR("EEPROM_WRITE_VAR addr=0x%04x size=0x%02hhx name=%s\n"), pos, size, name);
#endif //DEBUG_EEPROM_WRITE
while (size--)
{
eeprom_update_byte(pos, *value);
if (eeprom_read_byte(pos) != *value) {
SERIAL_ECHOLNPGM("EEPROM Error");
return false;
}
pos++;
value++;
}
return true;
}
#ifdef DEBUG_EEPROM_READ
static void EEPROM_readData(uint8_t* pos, uint8_t* value, uint8_t size, const char* name)
#else //DEBUG_EEPROM_READ
static void EEPROM_readData(uint8_t* pos, uint8_t* value, uint8_t size, const char*)
#endif //DEBUG_EEPROM_READ
{
#ifdef DEBUG_EEPROM_READ
printf_P(PSTR("EEPROM_READ_VAR addr=0x%04x size=0x%02hhx name=%s\n"), pos, size, name);
#endif //DEBUG_EEPROM_READ
while(size--)
{
*value = eeprom_read_byte(pos);
pos++;
value++;
}
}
#define EEPROM_VERSION "V2"
#ifdef EEPROM_SETTINGS
void Config_StoreSettings()
{
strcpy(cs.version,"000"); //!< invalidate data first @TODO use erase to save one erase cycle
if (EEPROM_writeData(reinterpret_cast<uint8_t*>(EEPROM_M500_base),reinterpret_cast<uint8_t*>(&cs),sizeof(cs),0), "cs, invalid version")
{
strcpy(cs.version,EEPROM_VERSION); //!< validate data if write succeed
EEPROM_writeData(reinterpret_cast<uint8_t*>(EEPROM_M500_base->version), reinterpret_cast<uint8_t*>(cs.version), sizeof(cs.version), "cs.version valid");
}
SERIAL_ECHO_START;
SERIAL_ECHOLNPGM("Settings Stored");
}
#endif //EEPROM_SETTINGS
#ifndef DISABLE_M503
void Config_PrintSettings(uint8_t level)
{ // Always have this function, even with EEPROM_SETTINGS disabled, the current values will be shown
#ifdef TMC2130
printf_P(PSTR(
"%SSteps per unit:\n%S M92 X%.2f Y%.2f Z%.2f E%.2f\n"
"%SUStep resolution: \n%S M350 X%d Y%d Z%d E%d\n"
"%SMaximum feedrates - normal (mm/s):\n%S M203 X%.2f Y%.2f Z%.2f E%.2f\n"
"%SMaximum feedrates - stealth (mm/s):\n%S M203 X%.2f Y%.2f Z%.2f E%.2f\n"
"%SMaximum acceleration - normal (mm/s2):\n%S M201 X%lu Y%lu Z%lu E%lu\n"
"%SMaximum acceleration - stealth (mm/s2):\n%S M201 X%lu Y%lu Z%lu E%lu\n"
"%SAcceleration: S=acceleration, T=retract acceleration\n%S M204 S%.2f T%.2f\n"
"%SAdvanced variables: S=Min feedrate (mm/s), T=Min travel feedrate (mm/s), B=minimum segment time (ms), X=maximum XY jerk (mm/s), Z=maximum Z jerk (mm/s), E=maximum E jerk (mm/s)\n%S M205 S%.2f T%.2f B%.2f X%.2f Y%.2f Z%.2f E%.2f\n"
"%SHome offset (mm):\n%S M206 X%.2f Y%.2f Z%.2f\n"
),
echomagic, echomagic, cs.axis_steps_per_unit[X_AXIS], cs.axis_steps_per_unit[Y_AXIS], cs.axis_steps_per_unit[Z_AXIS], cs.axis_steps_per_unit[E_AXIS],
echomagic, echomagic, cs.axis_ustep_resolution[X_AXIS], cs.axis_ustep_resolution[Y_AXIS], cs.axis_ustep_resolution[Z_AXIS], cs.axis_ustep_resolution[E_AXIS],
echomagic, echomagic, cs.max_feedrate_normal[X_AXIS], cs.max_feedrate_normal[Y_AXIS], cs.max_feedrate_normal[Z_AXIS], cs.max_feedrate_normal[E_AXIS],
echomagic, echomagic, cs.max_feedrate_silent[X_AXIS], cs.max_feedrate_silent[Y_AXIS], cs.max_feedrate_silent[Z_AXIS], cs.max_feedrate_silent[E_AXIS],
echomagic, echomagic, cs.max_acceleration_units_per_sq_second_normal[X_AXIS], cs.max_acceleration_units_per_sq_second_normal[Y_AXIS], cs.max_acceleration_units_per_sq_second_normal[Z_AXIS], cs.max_acceleration_units_per_sq_second_normal[E_AXIS],
echomagic, echomagic, cs.max_acceleration_units_per_sq_second_silent[X_AXIS], cs.max_acceleration_units_per_sq_second_silent[Y_AXIS], cs.max_acceleration_units_per_sq_second_silent[Z_AXIS], cs.max_acceleration_units_per_sq_second_silent[E_AXIS],
echomagic, echomagic, cs.acceleration, cs.retract_acceleration,
echomagic, echomagic, cs.minimumfeedrate, cs.mintravelfeedrate, cs.minsegmenttime, cs.max_jerk[X_AXIS], cs.max_jerk[Y_AXIS], cs.max_jerk[Z_AXIS], cs.max_jerk[E_AXIS],
echomagic, echomagic, cs.add_homing[X_AXIS], cs.add_homing[Y_AXIS], cs.add_homing[Z_AXIS]
#else //TMC2130
printf_P(PSTR(
"%SSteps per unit:\n%S M92 X%.2f Y%.2f Z%.2f E%.2f\n"
"%SMaximum feedrates (mm/s):\n%S M203 X%.2f Y%.2f Z%.2f E%.2f\n"
"%SMaximum acceleration (mm/s2):\n%S M201 X%lu Y%lu Z%lu E%lu\n"
"%SAcceleration: S=acceleration, T=retract acceleration\n%S M204 S%.2f T%.2f\n"
"%SAdvanced variables: S=Min feedrate (mm/s), T=Min travel feedrate (mm/s), B=minimum segment time (ms), X=maximum XY jerk (mm/s), Z=maximum Z jerk (mm/s), E=maximum E jerk (mm/s)\n%S M205 S%.2f T%.2f B%.2f X%.2f Y%.2f Z%.2f E%.2f\n"
"%SHome offset (mm):\n%S M206 X%.2f Y%.2f Z%.2f\n"
),
echomagic, echomagic, cs.axis_steps_per_unit[X_AXIS], cs.axis_steps_per_unit[Y_AXIS], cs.axis_steps_per_unit[Z_AXIS], cs.axis_steps_per_unit[E_AXIS],
echomagic, echomagic, max_feedrate[X_AXIS], max_feedrate[Y_AXIS], max_feedrate[Z_AXIS], max_feedrate[E_AXIS],
echomagic, echomagic, max_acceleration_units_per_sq_second[X_AXIS], max_acceleration_units_per_sq_second[Y_AXIS], max_acceleration_units_per_sq_second[Z_AXIS], max_acceleration_units_per_sq_second[E_AXIS],
echomagic, echomagic, cs.acceleration, cs.retract_acceleration,
echomagic, echomagic, cs.minimumfeedrate, cs.mintravelfeedrate, cs.minsegmenttime, cs.max_jerk[X_AXIS], cs.max_jerk[Y_AXIS], cs.max_jerk[Z_AXIS], cs.max_jerk[E_AXIS],
echomagic, echomagic, cs.add_homing[X_AXIS], cs.add_homing[Y_AXIS], cs.add_homing[Z_AXIS]
#endif //TMC2130
);
#ifdef PIDTEMP
printf_P(PSTR("%SPID settings:\n%S M301 P%.2f I%.2f D%.2f\n"),
echomagic, echomagic, cs.Kp, unscalePID_i(cs.Ki), unscalePID_d(cs.Kd));
#endif
#ifdef PIDTEMPBED
printf_P(PSTR("%SPID heatbed settings:\n%S M304 P%.2f I%.2f D%.2f\n"),
echomagic, echomagic, cs.bedKp, unscalePID_i(cs.bedKi), unscalePID_d(cs.bedKd));
#endif
#ifdef FWRETRACT
printf_P(PSTR(
"%SRetract: S=Length (mm) F:Speed (mm/m) Z: ZLift (mm)\n%S M207 S%.2f F%.2f Z%.2f\n"
"%SRecover: S=Extra length (mm) F:Speed (mm/m)\n%S M208 S%.2f F%.2f\n"
"%SAuto-Retract: S=0 to disable, 1 to interpret extrude-only moves as retracts or recoveries\n%S M209 S%d\n"
),
echomagic, echomagic, cs.retract_length, cs.retract_feedrate*60, cs.retract_zlift,
echomagic, echomagic, cs.retract_recover_length, cs.retract_recover_feedrate*60,
echomagic, echomagic, (cs.autoretract_enabled ? 1 : 0)
);
#if EXTRUDERS > 1
printf_P(PSTR("%SMulti-extruder settings:\n%S Swap retract length (mm): %.2f\n%S Swap rec. addl. length (mm): %.2f\n"),
echomagic, echomagic, retract_length_swap, echomagic, retract_recover_length_swap);
#endif
if (cs.volumetric_enabled) {
printf_P(PSTR("%SFilament settings:\n%S M200 D%.2f\n"),
echomagic, echomagic, cs.filament_size[0]);
#if EXTRUDERS > 1
printf_P(PSTR("%S M200 T1 D%.2f\n"),
echomagic, echomagic, cs.filament_size[1]);
#if EXTRUDERS > 2
printf_P(PSTR("%S M200 T1 D%.2f\n"),
echomagic, echomagic, cs.filament_size[2]);
#endif
#endif
} else {
printf_P(PSTR("%SFilament settings: Disabled\n"), echomagic);
}
#endif
if (level >= 10) {
#ifdef LIN_ADVANCE
printf_P(PSTR("%SLinear advance settings:\n M900 K%.2f E/D = %.2f\n"),
echomagic, extruder_advance_k, advance_ed_ratio);
#endif //LIN_ADVANCE
}
}
#endif
#ifdef EEPROM_SETTINGS
static_assert (EXTRUDERS == 1, "ConfigurationStore M500_conf not implemented for more extruders, fix filament_size array size.");
static_assert (NUM_AXIS == 4, "ConfigurationStore M500_conf not implemented for more axis."
"Fix axis_steps_per_unit max_feedrate_normal max_acceleration_units_per_sq_second_normal max_jerk max_feedrate_silent"
" max_acceleration_units_per_sq_second_silent array size.");
#ifdef ENABLE_AUTO_BED_LEVELING
static_assert (false, "zprobe_zoffset was not initialized in printers in field to -(Z_PROBE_OFFSET_FROM_EXTRUDER), so it contains"
"0.0, if this is not acceptable, increment EEPROM_VERSION to force use default_conf");
#endif
static_assert (sizeof(M500_conf) == 192, "sizeof(M500_conf) has changed, ensure that EEPROM_VERSION has been incremented, "
"or if you added members in the end of struct, ensure that historically uninitialized values will be initialized."
"If this is caused by change to more then 8bit processor, decide whether make this struct packed to save EEPROM,"
"leave as it is to keep fast code, or reorder struct members to pack more tightly.");
static const M500_conf default_conf PROGMEM =
{
EEPROM_VERSION,
DEFAULT_AXIS_STEPS_PER_UNIT,
DEFAULT_MAX_FEEDRATE,
DEFAULT_MAX_ACCELERATION,
DEFAULT_ACCELERATION,
DEFAULT_RETRACT_ACCELERATION,
DEFAULT_MINIMUMFEEDRATE,
DEFAULT_MINTRAVELFEEDRATE,
DEFAULT_MINSEGMENTTIME,
{DEFAULT_XJERK, DEFAULT_YJERK, DEFAULT_ZJERK, DEFAULT_EJERK},
{0,0,0},
-(Z_PROBE_OFFSET_FROM_EXTRUDER),
DEFAULT_Kp,
DEFAULT_Ki*PID_dT,
DEFAULT_Kd/PID_dT,
DEFAULT_bedKp,
DEFAULT_bedKi*PID_dT,
DEFAULT_bedKd/PID_dT,
0,
false,
RETRACT_LENGTH,
RETRACT_FEEDRATE,
RETRACT_ZLIFT,
RETRACT_RECOVER_LENGTH,
RETRACT_RECOVER_FEEDRATE,
false,
{DEFAULT_NOMINAL_FILAMENT_DIA,
#if EXTRUDERS > 1
DEFAULT_NOMINAL_FILAMENT_DIA,
#if EXTRUDERS > 2
DEFAULT_NOMINAL_FILAMENT_DIA,
#endif
#endif
},
DEFAULT_MAX_FEEDRATE_SILENT,
DEFAULT_MAX_ACCELERATION_SILENT,
#ifdef TMC2130
{ TMC2130_USTEPS_XY, TMC2130_USTEPS_XY, TMC2130_USTEPS_Z, TMC2130_USTEPS_E },
#else // TMC2130
{16,16,16,16},
#endif
};
//! @brief Read M500 configuration
//! @retval true Succeeded. Stored settings retrieved or default settings retrieved in case EEPROM has been erased.
//! @retval false Failed. Default settings has been retrieved, because of older version or corrupted data.
bool Config_RetrieveSettings()
{
bool previous_settings_retrieved = true;
char ver[4]=EEPROM_VERSION;
EEPROM_readData(reinterpret_cast<uint8_t*>(EEPROM_M500_base->version), reinterpret_cast<uint8_t*>(cs.version), sizeof(cs.version), "cs.version"); //read stored version
// SERIAL_ECHOLN("Version: [" << ver << "] Stored version: [" << cs.version << "]");
if (strncmp(ver,cs.version,3) == 0) // version number match
{
EEPROM_readData(reinterpret_cast<uint8_t*>(EEPROM_M500_base), reinterpret_cast<uint8_t*>(&cs), sizeof(cs), "cs");
if (cs.max_jerk[X_AXIS] > DEFAULT_XJERK) cs.max_jerk[X_AXIS] = DEFAULT_XJERK;
if (cs.max_jerk[Y_AXIS] > DEFAULT_YJERK) cs.max_jerk[Y_AXIS] = DEFAULT_YJERK;
calculate_extruder_multipliers();
//if max_feedrate_silent and max_acceleration_units_per_sq_second_silent were never stored to eeprom, use default values:
for (uint8_t i = 0; i < (sizeof(cs.max_feedrate_silent)/sizeof(cs.max_feedrate_silent[0])); ++i)
{
const uint32_t erased = 0xffffffff;
bool initialized = false;
for(uint8_t j = 0; j < sizeof(float); ++j)
{
if(0xff != reinterpret_cast<uint8_t*>(&(cs.max_feedrate_silent[i]))[j]) initialized = true;
}
if (!initialized) memcpy_P(&cs.max_feedrate_silent[i],&default_conf.max_feedrate_silent[i], sizeof(cs.max_feedrate_silent[i]));
if (erased == cs.max_acceleration_units_per_sq_second_silent[i]) {
memcpy_P(&cs.max_acceleration_units_per_sq_second_silent[i],&default_conf.max_acceleration_units_per_sq_second_silent[i],sizeof(cs.max_acceleration_units_per_sq_second_silent[i]));
}
}
#ifdef TMC2130
for (uint8_t j = X_AXIS; j <= Y_AXIS; j++)
{
if (cs.max_feedrate_normal[j] > NORMAL_MAX_FEEDRATE_XY)
cs.max_feedrate_normal[j] = NORMAL_MAX_FEEDRATE_XY;
if (cs.max_feedrate_silent[j] > SILENT_MAX_FEEDRATE_XY)
cs.max_feedrate_silent[j] = SILENT_MAX_FEEDRATE_XY;
if (cs.max_acceleration_units_per_sq_second_normal[j] > NORMAL_MAX_ACCEL_XY)
cs.max_acceleration_units_per_sq_second_normal[j] = NORMAL_MAX_ACCEL_XY;
if (cs.max_acceleration_units_per_sq_second_silent[j] > SILENT_MAX_ACCEL_XY)
cs.max_acceleration_units_per_sq_second_silent[j] = SILENT_MAX_ACCEL_XY;
}
if(cs.axis_ustep_resolution[X_AXIS] == 0xff){ cs.axis_ustep_resolution[X_AXIS] = TMC2130_USTEPS_XY; }
if(cs.axis_ustep_resolution[Y_AXIS] == 0xff){ cs.axis_ustep_resolution[Y_AXIS] = TMC2130_USTEPS_XY; }
if(cs.axis_ustep_resolution[Z_AXIS] == 0xff){ cs.axis_ustep_resolution[Z_AXIS] = TMC2130_USTEPS_Z; }
if(cs.axis_ustep_resolution[E_AXIS] == 0xff){ cs.axis_ustep_resolution[E_AXIS] = TMC2130_USTEPS_E; }
tmc2130_set_res(X_AXIS, cs.axis_ustep_resolution[X_AXIS]);
tmc2130_set_res(Y_AXIS, cs.axis_ustep_resolution[Y_AXIS]);
tmc2130_set_res(Z_AXIS, cs.axis_ustep_resolution[Z_AXIS]);
tmc2130_set_res(E_AXIS, cs.axis_ustep_resolution[E_AXIS]);
#endif //TMC2130
reset_acceleration_rates();
// Call updatePID (similar to when we have processed M301)
updatePID();
SERIAL_ECHO_START;
SERIAL_ECHOLNPGM("Stored settings retrieved");
}
else
{
Config_ResetDefault();
//Return false to inform user that eeprom version was changed and firmware is using default hardcoded settings now.
//In case that storing to eeprom was not used yet, do not inform user that hardcoded settings are used.
if (eeprom_read_byte(reinterpret_cast<uint8_t*>(&(EEPROM_M500_base->version[0]))) != 0xFF ||
eeprom_read_byte(reinterpret_cast<uint8_t*>(&(EEPROM_M500_base->version[1]))) != 0xFF ||
eeprom_read_byte(reinterpret_cast<uint8_t*>(&(EEPROM_M500_base->version[2]))) != 0xFF)
{
previous_settings_retrieved = false;
}
}
#ifdef EEPROM_CHITCHAT
Config_PrintSettings();
#endif
return previous_settings_retrieved;
}
#endif
void Config_ResetDefault()
{
memcpy_P(&cs,&default_conf, sizeof(cs));
// steps per sq second need to be updated to agree with the units per sq second
reset_acceleration_rates();
#ifdef PIDTEMP
updatePID();
#ifdef PID_ADD_EXTRUSION_RATE
Kc = DEFAULT_Kc; //this is not stored by Config_StoreSettings
#endif//PID_ADD_EXTRUSION_RATE
#endif//PIDTEMP
calculate_extruder_multipliers();
SERIAL_ECHO_START;
SERIAL_ECHOLNPGM("Hardcoded Default Settings Loaded");
}

View File

@ -0,0 +1,65 @@
#ifndef CONFIG_STORE_H
#define CONFIG_STORE_H
#define EEPROM_SETTINGS
#include "Configuration.h"
#include <stdint.h>
#include <avr/eeprom.h>
typedef struct
{
char version[4];
float axis_steps_per_unit[4];
float max_feedrate_normal[4];
unsigned long max_acceleration_units_per_sq_second_normal[4];
float acceleration; //!< Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX
float retract_acceleration; //!< mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX
float minimumfeedrate;
float mintravelfeedrate;
unsigned long minsegmenttime;
float max_jerk[4]; //!< Jerk is a maximum immediate velocity change.
float add_homing[3];
float zprobe_zoffset;
float Kp;
float Ki;
float Kd;
float bedKp;
float bedKi;
float bedKd;
int lcd_contrast; //!< unused
bool autoretract_enabled;
float retract_length;
float retract_feedrate;
float retract_zlift;
float retract_recover_length;
float retract_recover_feedrate;
bool volumetric_enabled;
float filament_size[1]; //!< cross-sectional area of filament (in millimeters), typically around 1.75 or 2.85, 0 disables the volumetric calculations for the extruder.
float max_feedrate_silent[4]; //!< max speeds for silent mode
unsigned long max_acceleration_units_per_sq_second_silent[4];
unsigned char axis_ustep_resolution[4];
} M500_conf;
extern M500_conf cs;
void Config_ResetDefault();
#ifndef DISABLE_M503
void Config_PrintSettings(uint8_t level = 0);
#else
FORCE_INLINE void Config_PrintSettings() {}
#endif
#ifdef EEPROM_SETTINGS
void Config_StoreSettings();
bool Config_RetrieveSettings();
#else
FORCE_INLINE void Config_StoreSettings() {}
FORCE_INLINE void Config_RetrieveSettings() { Config_ResetDefault(); Config_PrintSettings(); }
#endif
inline uint8_t calibration_status() { return eeprom_read_byte((uint8_t*)EEPROM_CALIBRATION_STATUS); }
inline void calibration_status_store(uint8_t status) { eeprom_update_byte((uint8_t*)EEPROM_CALIBRATION_STATUS, status); }
inline bool calibration_status_pinda() { return eeprom_read_byte((uint8_t*)EEPROM_CALIBRATION_STATUS_PINDA); }
#endif//CONFIG_STORE_H

View File

@ -0,0 +1,454 @@
#ifndef CONFIGURATION_ADV_H
#define CONFIGURATION_ADV_H
//===========================================================================
//=============================Thermal Settings ============================
//===========================================================================
#ifdef BED_LIMIT_SWITCHING
#define BED_HYSTERESIS 2 //only disable heating if T>target+BED_HYSTERESIS and enable heating if T>target-BED_HYSTERESIS
#endif
#define BED_CHECK_INTERVAL 5000 //ms between checks in bang-bang control
#ifdef PIDTEMP
// this adds an experimental additional term to the heating power, proportional to the extrusion speed.
// if Kc is chosen well, the additional required power due to increased melting should be compensated.
#define PID_ADD_EXTRUSION_RATE
#ifdef PID_ADD_EXTRUSION_RATE
#define DEFAULT_Kc (1) //heating power=Kc*(e_speed)
#endif
#endif
//automatic temperature: The hot end target temperature is calculated by all the buffered lines of gcode.
//The maximum buffered steps/sec of the extruder motor are called "se".
//You enter the autotemp mode by a M109 S<mintemp> B<maxtemp> F<factor>
// the target temperature is set to mintemp+factor*se[steps/sec] and limited by mintemp and maxtemp
// you exit the value by any M109 without F*
// Also, if the temperature is set to a value <mintemp, it is not changed by autotemp.
// on an Ultimaker, some initial testing worked with M109 S215 B260 F1 in the start.gcode
//#define AUTOTEMP
#ifdef AUTOTEMP
#define AUTOTEMP_OLDWEIGHT 0.98
#endif
//Show Temperature ADC value
//The M105 command return, besides traditional information, the ADC value read from temperature sensors.
//#define SHOW_TEMP_ADC_VALUES
// extruder run-out prevention.
//if the machine is idle, and the temperature over MINTEMP, every couple of SECONDS some filament is extruded
//#define EXTRUDER_RUNOUT_PREVENT
#define EXTRUDER_RUNOUT_MINTEMP 190
#define EXTRUDER_RUNOUT_SECONDS 30.
#define EXTRUDER_RUNOUT_ESTEPS 14. //mm filament
#define EXTRUDER_RUNOUT_SPEED 1500. //extrusion speed
#define EXTRUDER_RUNOUT_EXTRUDE 100
//These defines help to calibrate the AD595 sensor in case you get wrong temperature measurements.
//The measured temperature is defined as "actualTemp = (measuredTemp * TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET"
#define TEMP_SENSOR_AD595_OFFSET 0.0
#define TEMP_SENSOR_AD595_GAIN 1.0
//This is for controlling a fan to cool down the stepper drivers
//it will turn on when any driver is enabled
//and turn off after the set amount of seconds from last driver being disabled again
#define CONTROLLERFAN_PIN -1 //Pin used for the fan to cool controller (-1 to disable)
#define CONTROLLERFAN_SECS 60 //How many seconds, after all motors were disabled, the fan should run
#define CONTROLLERFAN_SPEED 255 // == full speed
// When first starting the main fan, run it at full speed for the
// given number of milliseconds. This gets the fan spinning reliably
// before setting a PWM value. (Does not work with software PWM for fan on Sanguinololu)
#define FAN_KICKSTART_TIME 800
//===========================================================================
//=============================Mechanical Settings===========================
//===========================================================================
#define ENDSTOPS_ONLY_FOR_HOMING // If defined the endstops will only be used for homing
//// AUTOSET LOCATIONS OF LIMIT SWITCHES
//// Added by ZetaPhoenix 09-15-2012
#ifdef MANUAL_HOME_POSITIONS // Use manual limit switch locations
#define X_HOME_POS MANUAL_X_HOME_POS
#define Y_HOME_POS MANUAL_Y_HOME_POS
#define Z_HOME_POS MANUAL_Z_HOME_POS
#else //Set min/max homing switch positions based upon homing direction and min/max travel limits
//X axis
#if X_HOME_DIR == -1
#ifdef BED_CENTER_AT_0_0
#define X_HOME_POS X_MAX_LENGTH * -0.5
#else
#define X_HOME_POS X_MIN_POS
#endif //BED_CENTER_AT_0_0
#else
#ifdef BED_CENTER_AT_0_0
#define X_HOME_POS X_MAX_LENGTH * 0.5
#else
#define X_HOME_POS X_MAX_POS
#endif //BED_CENTER_AT_0_0
#endif //X_HOME_DIR == -1
//Y axis
#if Y_HOME_DIR == -1
#ifdef BED_CENTER_AT_0_0
#define Y_HOME_POS Y_MAX_LENGTH * -0.5
#else
#define Y_HOME_POS Y_MIN_POS
#endif //BED_CENTER_AT_0_0
#else
#ifdef BED_CENTER_AT_0_0
#define Y_HOME_POS Y_MAX_LENGTH * 0.5
#else
#define Y_HOME_POS Y_MAX_POS
#endif //BED_CENTER_AT_0_0
#endif //Y_HOME_DIR == -1
// Z axis
#if Z_HOME_DIR == -1 //BED_CENTER_AT_0_0 not used
#define Z_HOME_POS Z_MIN_POS
#else
#define Z_HOME_POS Z_MAX_POS
#endif //Z_HOME_DIR == -1
#endif //End auto min/max positions
//END AUTOSET LOCATIONS OF LIMIT SWITCHES -ZP
// A single Z stepper driver is usually used to drive 2 stepper motors.
// Uncomment this define to utilize a separate stepper driver for each Z axis motor.
// Only a few motherboards support this, like RAMPS, which have dual extruder support (the 2nd, often unused, extruder driver is used
// to control the 2nd Z axis stepper motor). The pins are currently only defined for a RAMPS motherboards.
// On a RAMPS (or other 5 driver) motherboard, using this feature will limit you to using 1 extruder.
//#define Z_DUAL_STEPPER_DRIVERS
#ifdef Z_DUAL_STEPPER_DRIVERS
#undef EXTRUDERS
#define EXTRUDERS 1
#endif
// Same again but for Y Axis.
//#define Y_DUAL_STEPPER_DRIVERS
// Define if the two Y drives need to rotate in opposite directions
#define INVERT_Y2_VS_Y_DIR 1
#ifdef Y_DUAL_STEPPER_DRIVERS
#undef EXTRUDERS
#define EXTRUDERS 1
#endif
#if defined (Z_DUAL_STEPPER_DRIVERS) && defined (Y_DUAL_STEPPER_DRIVERS)
#error "You cannot have dual drivers for both Y and Z"
#endif
//homing hits the endstop, then retracts by this distance, before it tries to slowly bump again:
#define X_HOME_RETRACT_MM 5
#define Y_HOME_RETRACT_MM 5
#define Z_HOME_RETRACT_MM 2
//#define QUICK_HOME //if this is defined, if both x and y are to be homed, a diagonal move will be performed initially.
#define AXIS_RELATIVE_MODES {0, 0, 0, 0}
#define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step). Toshiba steppers are 4x slower, but Prusa3D does not use those.
//By default pololu step drivers require an active high signal. However, some high power drivers require an active low signal as step.
#define INVERT_X_STEP_PIN 0
#define INVERT_Y_STEP_PIN 0
#define INVERT_Z_STEP_PIN 0
#define INVERT_E_STEP_PIN 0
//default stepper release if idle
#define DEFAULT_STEPPER_DEACTIVE_TIME 60
#define DEFAULT_MINIMUMFEEDRATE 0.0 // minimum feedrate
#define DEFAULT_MINTRAVELFEEDRATE 0.0
// Feedrates for manual moves along X, Y, Z, E from panel
//Comment to disable setting feedrate multiplier via encoder
#define ULTIPANEL_FEEDMULTIPLY
// minimum time in microseconds that a movement needs to take if the buffer is emptied.
#define DEFAULT_MINSEGMENTTIME 20000
// If defined the movements slow down when the look ahead buffer is only half full
#define SLOWDOWN
// 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
// Microstep setting (Only functional when stepper driver microstep pins are connected to MCU.
#define MICROSTEP_MODES {16,16,16,16,16} // [1,2,4,8,16]
//===========================================================================
//=============================Additional Features===========================
//===========================================================================
//#define CHDK 4 //Pin for triggering CHDK to take a picture see how to use it here http://captain-slow.dk/2014/03/09/3d-printing-timelapses/
#define CHDK_DELAY 50 //How long in ms the pin should stay HIGH before going LOW again
#define SD_FINISHED_STEPPERRELEASE 1 //if sd support and the file is finished: disable steppers?
#define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place.
#define SDCARD_RATHERRECENTFIRST //reverse file order of sd card menu display. Its sorted practically after the file system block order.
// if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that.
// using:
//#define MENU_ADDAUTOSTART
/**
* Sort SD file listings in alphabetical order.
*
* With this option enabled, items on SD cards will be sorted
* by name for easier navigation.
*
* By default...
*
* - Use the slowest -but safest- method for sorting.
* - Folders are sorted to the top.
* - The sort key is statically allocated.
* - No added G-code (M34) support.
* - 40 item sorting limit. (Items after the first 40 are unsorted.)
*
* SD sorting uses static allocation (as set by SDSORT_LIMIT), allowing the
* compiler to calculate the worst-case usage and throw an error if the SRAM
* limit is exceeded.
*
* - SDSORT_USES_RAM provides faster sorting via a static directory buffer.
* - SDSORT_USES_STACK does the same, but uses a local stack-based buffer.
* - SDSORT_CACHE_NAMES will retain the sorted file listing in RAM. (Expensive!)
* - SDSORT_DYNAMIC_RAM only uses RAM when the SD menu is visible. (Use with caution!)
*/
#define SDCARD_SORT_ALPHA //Alphabetical sorting of SD files menu
// SD Card Sorting options
// In current firmware Prusa Firmware version,
// SDSORT_CACHE_NAMES and SDSORT_DYNAMIC_RAM is not supported and must be set to 0.
#ifdef SDCARD_SORT_ALPHA
#define SD_SORT_TIME 0
#define SD_SORT_ALPHA 1
#define SD_SORT_NONE 2
#define SDSORT_LIMIT 100 // Maximum number of sorted items (10-256).
#define FOLDER_SORTING -1 // -1=above 0=none 1=below
#define SDSORT_GCODE 0 // Allow turning sorting on/off with LCD and M34 g-code.
#define SDSORT_USES_RAM 0 // Pre-allocate a static array for faster pre-sorting.
#define SDSORT_USES_STACK 0 // Prefer the stack for pre-sorting to give back some SRAM. (Negated by next 2 options.)
#define SDSORT_CACHE_NAMES 0 // Keep sorted items in RAM longer for speedy performance. Most expensive option.
#define SDSORT_DYNAMIC_RAM 0 // Use dynamic allocation (within SD menus). Least expensive option. Set SDSORT_LIMIT before use!
#endif
#if defined(SDCARD_SORT_ALPHA)
#define HAS_FOLDER_SORTING (FOLDER_SORTING || SDSORT_GCODE)
#endif
// Enable the option to stop SD printing when hitting and endstops, needs to be enabled from the LCD menu when this option is enabled.
//#define ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED
// Babystepping enables the user to control the axis in tiny amounts, independently from the normal printing process
// it can e.g. be used to change z-positions in the print startup phase in real-time
// does not respect endstops!
#define BABYSTEPPING
#ifdef BABYSTEPPING
#define BABYSTEP_XY //not only z, but also XY in the menu. more clutter, more functions
#define BABYSTEP_INVERT_Z 0 //1 for inverse movements in Z
#define BABYSTEP_Z_MULTIPLICATOR 2 //faster z movements
#ifdef COREXY
#error BABYSTEPPING not implemented for COREXY yet.
#endif
#endif
/**
* Implementation of linear pressure control
*
* Assumption: advance = k * (delta velocity)
* K=0 means advance disabled.
* See Marlin documentation for calibration instructions.
*/
#define LIN_ADVANCE
#ifdef LIN_ADVANCE
#define LIN_ADVANCE_K 0 //Try around 45 for PLA, around 25 for ABS.
/**
* Some Slicers produce Gcode with randomly jumping extrusion widths occasionally.
* For example within a 0.4mm perimeter it may produce a single segment of 0.05mm width.
* While this is harmless for normal printing (the fluid nature of the filament will
* close this very, very tiny gap), it throws off the LIN_ADVANCE pressure adaption.
*
* For this case LIN_ADVANCE_E_D_RATIO can be used to set the extrusion:distance ratio
* to a fixed value. Note that using a fixed ratio will lead to wrong nozzle pressures
* if the slicer is using variable widths or layer heights within one print!
*
* This option sets the default E:D ratio at startup. Use `M900` to override this value.
*
* Example: `M900 W0.4 H0.2 D1.75`, where:
* - W is the extrusion width in mm
* - H is the layer height in mm
* - D is the filament diameter in mm
*
* Example: `M900 R0.0458` to set the ratio directly.
*
* Set to 0 to auto-detect the ratio based on given Gcode G1 print moves.
*
* Slic3r (including Prusa Slic3r) produces Gcode compatible with the automatic mode.
* Cura (as of this writing) may produce Gcode incompatible with the automatic mode.
*/
#define LIN_ADVANCE_E_D_RATIO 0 // The calculated ratio (or 0) according to the formula W * H / ((D / 2) ^ 2 * PI)
// Example: 0.4 * 0.2 / ((1.75 / 2) ^ 2 * PI) = 0.033260135
#endif
// Arc interpretation settings:
#define MM_PER_ARC_SEGMENT 1
#define N_ARC_CORRECTION 25
const unsigned int dropsegments=5; //everything with less than this number of steps will be ignored as move and joined with the next movement
// If you are using a RAMPS board or cheap E-bay purchased boards that do not detect when an SD card is inserted
// You can get round this by connecting a push button or single throw switch to the pin defined as SDCARDCARDDETECT
// in the pins.h file. When using a push button pulling the pin to ground this will need inverted. This setting should
// be commented out otherwise
#define SDCARDDETECTINVERTED
#undef SDCARDDETECTINVERTED
// Power Signal Control Definitions
// By default use ATX definition
#ifndef POWER_SUPPLY
#define POWER_SUPPLY 1
#endif
// 1 = ATX
#if (POWER_SUPPLY == 1)
#define PS_ON_AWAKE LOW
#define PS_ON_ASLEEP HIGH
#endif
// 2 = X-Box 360 203W
#if (POWER_SUPPLY == 2)
#define PS_ON_AWAKE HIGH
#define PS_ON_ASLEEP LOW
#endif
// Control heater 0 and heater 1 in parallel.
//#define HEATERS_PARALLEL
//===========================================================================
//=============================Buffers ============================
//===========================================================================
// The number of linear motions that can be in the plan at any give time.
// THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2, i.g. 8,16,32 because shifts and ors are used to do the ring-buffering.
#if defined SDSUPPORT
#define BLOCK_BUFFER_SIZE 16 // SD,LCD,Buttons take more memory, block buffer needs to be smaller
#else
#define BLOCK_BUFFER_SIZE 16 // maximize block buffer
#endif
//The ASCII buffer for receiving from the serial:
#define MAX_CMD_SIZE 96
#define BUFSIZE 4
// The command header contains the following values:
// 1st byte: the command source (CMDBUFFER_CURRENT_TYPE_USB, CMDBUFFER_CURRENT_TYPE_SDCARD, CMDBUFFER_CURRENT_TYPE_UI or CMDBUFFER_CURRENT_TYPE_CHAINED)
// 2nd and 3rd byte (LSB first) contains a 16bit length of a command including its preceding comments.
#define CMDHDRSIZE 3
// Firmware based and LCD controlled retract
// M207 and M208 can be used to define parameters for the retraction.
// The retraction can be called by the slicer using G10 and G11
// until then, intended retractions can be detected by moves that only extrude and the direction.
// the moves are than replaced by the firmware controlled ones.
#define FWRETRACT //ONLY PARTIALLY TESTED
#ifdef FWRETRACT
#define MIN_RETRACT 0.1 //minimum extruded mm to accept a automatic gcode retraction attempt
#define RETRACT_LENGTH 3 //default retract length (positive mm)
#define RETRACT_LENGTH_SWAP 13 //default swap retract length (positive mm), for extruder change
#define RETRACT_FEEDRATE 45 //default feedrate for retracting (mm/s)
#define RETRACT_ZLIFT 0 //default retract Z-lift
#define RETRACT_RECOVER_LENGTH 0 //default additional recover length (mm, added to retract length when recovering)
#define RETRACT_RECOVER_LENGTH_SWAP 0 //default additional swap recover length (mm, added to retract length when recovering from extruder change)
#define RETRACT_RECOVER_FEEDRATE 8 //default feedrate for recovering from retraction (mm/s)
#endif
//adds support for experimental filament exchange support M600; requires display
#ifdef FILAMENTCHANGEENABLE
#ifdef EXTRUDER_RUNOUT_PREVENT
#error EXTRUDER_RUNOUT_PREVENT currently incompatible with FILAMENTCHANGE
#endif
#endif
//===========================================================================
//============================= Define Defines ============================
//===========================================================================
#if EXTRUDERS > 1 && defined HEATERS_PARALLEL
#error "You cannot use HEATERS_PARALLEL if EXTRUDERS > 1"
#endif
#if TEMP_SENSOR_0 > 0
#define THERMISTORHEATER_0 TEMP_SENSOR_0
#define HEATER_0_USES_THERMISTOR
#endif
#if TEMP_SENSOR_1 > 0
#define THERMISTORHEATER_1 TEMP_SENSOR_1
#define HEATER_1_USES_THERMISTOR
#endif
#if TEMP_SENSOR_2 > 0
#define THERMISTORHEATER_2 TEMP_SENSOR_2
#define HEATER_2_USES_THERMISTOR
#endif
#if TEMP_SENSOR_BED > 0
#define THERMISTORBED TEMP_SENSOR_BED
#define BED_USES_THERMISTOR
#endif
#if TEMP_SENSOR_PINDA > 0
#define THERMISTORPINDA TEMP_SENSOR_PINDA
#endif
#if TEMP_SENSOR_AMBIENT > 0
#define THERMISTORAMBIENT TEMP_SENSOR_AMBIENT
#endif
#if TEMP_SENSOR_0 == -1
#define HEATER_0_USES_AD595
#endif
#if TEMP_SENSOR_1 == -1
#define HEATER_1_USES_AD595
#endif
#if TEMP_SENSOR_2 == -1
#define HEATER_2_USES_AD595
#endif
#if TEMP_SENSOR_BED == -1
#define BED_USES_AD595
#endif
#if TEMP_SENSOR_0 == -2
#define HEATER_0_USES_MAX6675
#endif
#if TEMP_SENSOR_0 == 0
#undef HEATER_0_MINTEMP
#undef HEATER_0_MAXTEMP
#endif
#if TEMP_SENSOR_1 == 0
#undef HEATER_1_MINTEMP
#undef HEATER_1_MAXTEMP
#endif
#if TEMP_SENSOR_2 == 0
#undef HEATER_2_MINTEMP
#undef HEATER_2_MAXTEMP
#endif
#if TEMP_SENSOR_BED == 0
#undef BED_MINTEMP
#undef BED_MAXTEMP
#endif
#endif //__CONFIGURATION_ADV_H

680
Firmware/Dcodes.cpp Normal file
View File

@ -0,0 +1,680 @@
#include "Dcodes.h"
//#include "Marlin.h"
#include "language.h"
#include "cmdqueue.h"
#include <stdio.h>
#include <avr/pgmspace.h>
#define SHOW_TEMP_ADC_VALUES
#include "temperature.h"
#define DBG(args...) printf_P(args)
inline void print_hex_nibble(uint8_t val)
{
putchar((val > 9)?(val - 10 + 'a'):(val + '0'));
}
void print_hex_byte(uint8_t val)
{
print_hex_nibble(val >> 4);
print_hex_nibble(val & 15);
}
void print_hex_word(uint16_t val)
{
print_hex_byte(val >> 8);
print_hex_byte(val & 255);
}
void print_eeprom(uint16_t address, uint16_t count, uint8_t countperline = 16)
{
while (count)
{
print_hex_word(address);
putchar(' ');
uint8_t count_line = countperline;
while (count && count_line)
{
putchar(' ');
print_hex_byte(eeprom_read_byte((uint8_t*)address++));
count_line--;
count--;
}
putchar('\n');
}
}
int parse_hex(char* hex, uint8_t* data, int count)
{
int parsed = 0;
while (*hex)
{
if (count && (parsed >= count)) break;
char c = *(hex++);
if (c == ' ') continue;
if (c == '\n') break;
uint8_t val = 0x00;
if ((c >= '0') && (c <= '9')) val |= ((c - '0') << 4);
else if ((c >= 'a') && (c <= 'f')) val |= ((c - 'a' + 10) << 4);
else return -parsed;
c = *(hex++);
if ((c >= '0') && (c <= '9')) val |= (c - '0');
else if ((c >= 'a') && (c <= 'f')) val |= (c - 'a' + 10);
else return -parsed;
data[parsed] = val;
parsed++;
}
return parsed;
}
void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperline = 16)
{
while (count)
{
if (type == 2)
print_hex_nibble(address >> 16);
print_hex_word(address);
putchar(' ');
uint8_t count_line = countperline;
while (count && count_line)
{
uint8_t data = 0;
switch (type)
{
case 0: data = *((uint8_t*)address++); break;
case 1: data = eeprom_read_byte((uint8_t*)address++); break;
case 2: data = pgm_read_byte_far((uint8_t*)address++); break;
}
putchar(' ');
print_hex_byte(data);
count_line--;
count--;
}
putchar('\n');
}
}
#ifdef DEBUG_DCODE3
#define EEPROM_SIZE 0x1000
void dcode_3()
{
DBG(_N("D3 - Read/Write EEPROM\n"));
uint16_t address = 0x0000; //default 0x0000
uint16_t count = EEPROM_SIZE; //default 0x1000 (entire eeprom)
if (code_seen('A')) // Address (0x0000-0x0fff)
address = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value();
if (code_seen('C')) // Count (0x0001-0x1000)
count = (int)code_value();
address &= 0x1fff;
if (count > EEPROM_SIZE) count = EEPROM_SIZE;
if ((address + count) > EEPROM_SIZE) count = EEPROM_SIZE - address;
if (code_seen('X')) // Data
{
uint8_t data[16];
count = parse_hex(strchr_pointer + 1, data, 16);
if (count > 0)
{
for (uint16_t i = 0; i < count; i++)
eeprom_write_byte((uint8_t*)(address + i), data[i]);
printf_P(_N("%d bytes written to EEPROM at address 0x%04x"), count, address);
putchar('\n');
}
else
count = 0;
}
print_mem(address, count, 1);
/* while (count)
{
print_hex_word(address);
putchar(' ');
uint8_t countperline = 16;
while (count && countperline)
{
uint8_t data = eeprom_read_byte((uint8_t*)address++);
putchar(' ');
print_hex_byte(data);
countperline--;
count--;
}
putchar('\n');
}*/
}
#endif //DEBUG_DCODE3
#include "ConfigurationStore.h"
#include "cmdqueue.h"
#include "pat9125.h"
#include "adc.h"
#include "temperature.h"
#include <avr/wdt.h>
#include "bootapp.h"
#if 0
#define FLASHSIZE 0x40000
#define RAMSIZE 0x2000
#define boot_src_addr (*((uint32_t*)(RAMSIZE - 16)))
#define boot_dst_addr (*((uint32_t*)(RAMSIZE - 12)))
#define boot_copy_size (*((uint16_t*)(RAMSIZE - 8)))
#define boot_reserved (*((uint8_t*)(RAMSIZE - 6)))
#define boot_app_flags (*((uint8_t*)(RAMSIZE - 5)))
#define boot_app_magic (*((uint32_t*)(RAMSIZE - 4)))
#define BOOT_APP_FLG_ERASE 0x01
#define BOOT_APP_FLG_COPY 0x02
#define BOOT_APP_FLG_FLASH 0x04
extern uint8_t fsensor_log;
extern float current_temperature_pinda;
extern float axis_steps_per_unit[NUM_AXIS];
#define LOG(args...) printf(args)
#endif //0
#define LOG(args...)
void dcode__1()
{
printf_P(PSTR("D-1 - Endless loop\n"));
// cli();
while (1);
}
#ifdef DEBUG_DCODES
void dcode_0()
{
if (*(strchr_pointer + 1) == 0) return;
LOG("D0 - Reset\n");
if (code_seen('B')) //bootloader
{
cli();
wdt_enable(WDTO_15MS);
while(1);
}
else //reset
{
#ifndef _NO_ASM
asm volatile("jmp 0x00000");
#endif //_NO_ASM
}
}
void dcode_1()
{
LOG("D1 - Clear EEPROM and RESET\n");
cli();
for (int i = 0; i < 8192; i++)
eeprom_write_byte((unsigned char*)i, (unsigned char)0xff);
wdt_enable(WDTO_15MS);
while(1);
}
void dcode_2()
{
LOG("D2 - Read/Write RAM\n");
uint16_t address = 0x0000; //default 0x0000
uint16_t count = 0x2000; //default 0x2000 (entire ram)
if (code_seen('A')) // Address (0x0000-0x1fff)
address = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value();
if (code_seen('C')) // Count (0x0001-0x2000)
count = (int)code_value();
address &= 0x1fff;
if (count > 0x2000) count = 0x2000;
if ((address + count) > 0x2000) count = 0x2000 - address;
if (code_seen('X')) // Data
{
uint8_t data[16];
count = parse_hex(strchr_pointer + 1, data, 16);
if (count > 0)
{
for (uint16_t i = 0; i < count; i++)
*((uint8_t*)(address + i)) = data[i];
LOG("%d bytes written to RAM at address %04x", count, address);
}
else
count = 0;
}
print_mem(address, count, 0);
/* while (count)
{
print_hex_word(address);
putchar(' ');
uint8_t countperline = 16;
while (count && countperline)
{
uint8_t data = *((uint8_t*)address++);
putchar(' ');
print_hex_byte(data);
countperline--;
count--;
}
putchar('\n');
}*/
}
void dcode_4()
{
LOG("D4 - Read/Write PIN\n");
if (code_seen('P')) // Pin (0-255)
{
int pin = (int)code_value();
if ((pin >= 0) && (pin <= 255))
{
if (code_seen('F')) // Function in/out (0/1)
{
int fnc = (int)code_value();
if (fnc == 0) pinMode(pin, INPUT);
else if (fnc == 1) pinMode(pin, OUTPUT);
}
if (code_seen('V')) // Value (0/1)
{
int val = (int)code_value();
if (val == 0) digitalWrite(pin, LOW);
else if (val == 1) digitalWrite(pin, HIGH);
}
else
{
int val = (digitalRead(pin) != LOW)?1:0;
printf("PIN%d=%d", pin, val);
}
}
}
}
#endif //DEBUG_DCODES
#ifdef DEBUG_DCODE5
void dcode_5()
{
printf_P(PSTR("D5 - Read/Write FLASH\n"));
uint32_t address = 0x0000; //default 0x0000
uint16_t count = 0x0400; //default 0x0400 (1kb block)
if (code_seen('A')) // Address (0x00000-0x3ffff)
address = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value();
if (code_seen('C')) // Count (0x0001-0x2000)
count = (int)code_value();
address &= 0x3ffff;
if (count > 0x2000) count = 0x2000;
if ((address + count) > 0x40000) count = 0x40000 - address;
bool bErase = false;
bool bCopy = false;
if (code_seen('E')) //Erase
bErase = true;
uint8_t data[16];
if (code_seen('X')) // Data
{
count = parse_hex(strchr_pointer + 1, data, 16);
if (count > 0) bCopy = true;
}
if (bErase || bCopy)
{
if (bErase)
{
printf_P(PSTR("%d bytes of FLASH at address %05x will be erased\n"), count, address);
}
if (bCopy)
{
printf_P(PSTR("%d bytes will be written to FLASH at address %05x\n"), count, address);
}
cli();
boot_app_magic = 0x55aa55aa;
boot_app_flags = (bErase?(BOOT_APP_FLG_ERASE):0) | (bCopy?(BOOT_APP_FLG_COPY):0);
boot_copy_size = (uint16_t)count;
boot_dst_addr = (uint32_t)address;
boot_src_addr = (uint32_t)(&data);
bootapp_print_vars();
wdt_enable(WDTO_15MS);
while(1);
}
while (count)
{
print_hex_nibble(address >> 16);
print_hex_word(address);
putchar(' ');
uint8_t countperline = 16;
while (count && countperline)
{
uint8_t data = pgm_read_byte_far((uint8_t*)address++);
putchar(' ');
print_hex_byte(data);
countperline--;
count--;
}
putchar('\n');
}
}
#endif //DEBUG_DCODE5
#ifdef DEBUG_DCODES
void dcode_6()
{
LOG("D6 - Read/Write external FLASH\n");
}
void dcode_7()
{
LOG("D7 - Read/Write Bootloader\n");
/*
cli();
boot_app_magic = 0x55aa55aa;
boot_app_flags = BOOT_APP_FLG_ERASE | BOOT_APP_FLG_COPY | BOOT_APP_FLG_FLASH;
boot_copy_size = (uint16_t)0xc00;
boot_src_addr = (uint32_t)0x0003e400;
boot_dst_addr = (uint32_t)0x0003f400;
wdt_enable(WDTO_15MS);
while(1);
*/
}
void dcode_8()
{
printf_P(PSTR("D8 - Read/Write PINDA\n"));
uint8_t cal_status = calibration_status_pinda();
float temp_pinda = current_temperature_pinda;
float offset_z = temp_compensation_pinda_thermistor_offset(temp_pinda);
if ((strchr_pointer[1+1] == '?') || (strchr_pointer[1+1] == 0))
{
printf_P(PSTR("cal_status=%d\n"), cal_status?1:0);
for (uint8_t i = 0; i < 6; i++)
{
uint16_t offs = 0;
if (i > 0) offs = eeprom_read_word(((uint16_t*)EEPROM_PROBE_TEMP_SHIFT) + (i - 1));
float foffs = ((float)offs) / cs.axis_steps_per_unit[Z_AXIS];
offs = 1000 * foffs;
printf_P(PSTR("temp_pinda=%dC temp_shift=%dum\n"), 35 + i * 5, offs);
}
}
else if (strchr_pointer[1+1] == '!')
{
cal_status = 1;
eeprom_write_byte((uint8_t*)EEPROM_CALIBRATION_STATUS_PINDA, cal_status);
eeprom_write_word(((uint16_t*)EEPROM_PROBE_TEMP_SHIFT) + 0, 8); //40C - 20um - 8usteps
eeprom_write_word(((uint16_t*)EEPROM_PROBE_TEMP_SHIFT) + 1, 24); //45C - 60um - 24usteps
eeprom_write_word(((uint16_t*)EEPROM_PROBE_TEMP_SHIFT) + 2, 48); //50C - 120um - 48usteps
eeprom_write_word(((uint16_t*)EEPROM_PROBE_TEMP_SHIFT) + 3, 80); //55C - 200um - 80usteps
eeprom_write_word(((uint16_t*)EEPROM_PROBE_TEMP_SHIFT) + 4, 120); //60C - 300um - 120usteps
}
else
{
if (code_seen('P')) // Pinda temperature [C]
temp_pinda = code_value();
offset_z = temp_compensation_pinda_thermistor_offset(temp_pinda);
if (code_seen('Z')) // Z Offset [mm]
{
offset_z = code_value();
}
}
printf_P(PSTR("temp_pinda=%d offset_z=%d.%03d\n"), (int)temp_pinda, (int)offset_z, ((int)(1000 * offset_z) % 1000));
}
const char* dcode_9_ADC_name(uint8_t i)
{
switch (i)
{
case 0: return PSTR("TEMP_HEATER0");
case 1: return PSTR("TEMP_HEATER1");
case 2: return PSTR("TEMP_BED");
case 3: return PSTR("TEMP_PINDA");
case 4: return PSTR("VOLT_PWR");
case 5: return PSTR("TEMP_AMBIENT");
case 6: return PSTR("VOLT_BED");
}
return 0;
}
#ifdef AMBIENT_THERMISTOR
extern int current_temperature_raw_ambient;
#endif //AMBIENT_THERMISTOR
#ifdef VOLT_PWR_PIN
extern int current_voltage_raw_pwr;
#endif //VOLT_PWR_PIN
#ifdef VOLT_BED_PIN
extern int current_voltage_raw_bed;
#endif //VOLT_BED_PIN
uint16_t dcode_9_ADC_val(uint8_t i)
{
switch (i)
{
case 0: return current_temperature_raw[0];
case 1: return 0;
case 2: return current_temperature_bed_raw;
case 3: return current_temperature_raw_pinda;
#ifdef VOLT_PWR_PIN
case 4: return current_voltage_raw_pwr;
#endif //VOLT_PWR_PIN
#ifdef AMBIENT_THERMISTOR
case 5: return current_temperature_raw_ambient;
#endif //AMBIENT_THERMISTOR
#ifdef VOLT_BED_PIN
case 6: return current_voltage_raw_bed;
#endif //VOLT_BED_PIN
}
return 0;
}
void dcode_9()
{
printf_P(PSTR("D9 - Read/Write ADC\n"));
if ((strchr_pointer[1+1] == '?') || (strchr_pointer[1+1] == 0))
{
for (uint8_t i = 0; i < ADC_CHAN_CNT; i++)
printf_P(PSTR("\tADC%d=%4d\t(%S)\n"), i, dcode_9_ADC_val(i) >> 4, dcode_9_ADC_name(i));
}
else
{
uint8_t index = 0xff;
if (code_seen('I')) // index (index of used channel, not avr channel index)
index = code_value();
if (index < ADC_CHAN_CNT)
{
if (code_seen('V')) // value to be written as simulated
{
adc_sim_mask |= (1 << index);
adc_values[index] = (((int)code_value()) << 4);
printf_P(PSTR("ADC%d=%4d\n"), index, adc_values[index] >> 4);
}
}
}
}
void dcode_10()
{//Tell the printer that XYZ calibration went OK
LOG("D10 - XYZ calibration = OK\n");
calibration_status_store(CALIBRATION_STATUS_LIVE_ADJUST);
}
void dcode_12()
{//Time
LOG("D12 - Time\n");
}
#ifdef TMC2130
#include "planner.h"
#include "tmc2130.h"
extern void st_synchronize();
/**
* @brief D2130 Trinamic stepper controller
* D2130<axis><command>[subcommand][value]
* * Axis
* * * 'X'
* * * 'Y'
* * * 'Z'
* * * 'E'
* * command
* * * '0' current off
* * * '1' current on
* * * '+' single step
* * * * value sereval steps
* * * '-' dtto oposite direction
* * * '?' read register
* * * * "mres"
* * * * "step"
* * * * "mscnt"
* * * * "mscuract"
* * * * "wave"
* * * '!' set register
* * * * "mres"
* * * * "step"
* * * * "wave"
* * * * *0, 180..250 meaning: off, 0.9..1.25, recommended value is 1.1
* * * '@' home calibrate axis
*
* Example:
* D2130E?wave //print extruder microstep linearity compensation curve
* D2130E!wave0 //disable extruder linearity compensation curve, (sine curve is used)
* D2130E!wave220 // (sin(x))^1.1 extruder microstep compensation curve used
*/
void dcode_2130()
{
printf_P(PSTR("D2130 - TMC2130\n"));
uint8_t axis = 0xff;
switch (strchr_pointer[1+4])
{
case 'X': axis = X_AXIS; break;
case 'Y': axis = Y_AXIS; break;
case 'Z': axis = Z_AXIS; break;
case 'E': axis = E_AXIS; break;
}
if (axis != 0xff)
{
char ch_axis = strchr_pointer[1+4];
if (strchr_pointer[1+5] == '0') { tmc2130_set_pwr(axis, 0); }
else if (strchr_pointer[1+5] == '1') { tmc2130_set_pwr(axis, 1); }
else if (strchr_pointer[1+5] == '+')
{
if (strchr_pointer[1+6] == 0)
{
tmc2130_set_dir(axis, 0);
tmc2130_do_step(axis);
}
else
{
uint8_t steps = atoi(strchr_pointer + 1 + 6);
tmc2130_do_steps(axis, steps, 0, 1000);
}
}
else if (strchr_pointer[1+5] == '-')
{
if (strchr_pointer[1+6] == 0)
{
tmc2130_set_dir(axis, 1);
tmc2130_do_step(axis);
}
else
{
uint8_t steps = atoi(strchr_pointer + 1 + 6);
tmc2130_do_steps(axis, steps, 1, 1000);
}
}
else if (strchr_pointer[1+5] == '?')
{
if (strcmp(strchr_pointer + 7, "mres") == 0) printf_P(PSTR("%c mres=%d\n"), ch_axis, tmc2130_mres[axis]);
else if (strcmp(strchr_pointer + 7, "step") == 0) printf_P(PSTR("%c step=%d\n"), ch_axis, tmc2130_rd_MSCNT(axis) >> tmc2130_mres[axis]);
else if (strcmp(strchr_pointer + 7, "mscnt") == 0) printf_P(PSTR("%c MSCNT=%d\n"), ch_axis, tmc2130_rd_MSCNT(axis));
else if (strcmp(strchr_pointer + 7, "mscuract") == 0)
{
uint32_t val = tmc2130_rd_MSCURACT(axis);
int curA = (val & 0xff);
int curB = ((val >> 16) & 0xff);
if ((val << 7) & 0x8000) curA -= 256;
if ((val >> 9) & 0x8000) curB -= 256;
printf_P(PSTR("%c MSCURACT=0x%08lx A=%d B=%d\n"), ch_axis, val, curA, curB);
}
else if (strcmp(strchr_pointer + 7, "wave") == 0)
{
tmc2130_get_wave(axis, 0, stdout);
}
}
else if (strchr_pointer[1+5] == '!')
{
if (strncmp(strchr_pointer + 7, "step", 4) == 0)
{
uint8_t step = atoi(strchr_pointer + 11);
uint16_t res = tmc2130_get_res(axis);
tmc2130_goto_step(axis, step & (4*res - 1), 2, 1000, res);
}
else if (strncmp(strchr_pointer + 7, "mres", 4) == 0)
{
uint8_t mres = strchr_pointer[11] - '0';
if (mres <= 8)
{
st_synchronize();
uint16_t res = tmc2130_get_res(axis);
uint16_t res_new = tmc2130_mres2usteps(mres);
tmc2130_set_res(axis, res_new);
if (res_new > res)
cs.axis_steps_per_unit[axis] *= (res_new / res);
else
cs.axis_steps_per_unit[axis] /= (res / res_new);
}
}
else if (strncmp(strchr_pointer + 7, "wave", 4) == 0)
{
uint8_t fac1000 = atoi(strchr_pointer + 11) & 0xffff;
if (fac1000 < TMC2130_WAVE_FAC1000_MIN) fac1000 = 0;
if (fac1000 > TMC2130_WAVE_FAC1000_MAX) fac1000 = TMC2130_WAVE_FAC1000_MAX;
tmc2130_set_wave(axis, 247, fac1000);
tmc2130_wave_fac[axis] = fac1000;
}
}
else if (strchr_pointer[1+5] == '@')
{
tmc2130_home_calibrate(axis);
}
}
}
#endif //TMC2130
#ifdef PAT9125
void dcode_9125()
{
LOG("D9125 - PAT9125\n");
if ((strchr_pointer[1+4] == '?') || (strchr_pointer[1+4] == 0))
{
// printf("res_x=%d res_y=%d x=%d y=%d b=%d s=%d\n", pat9125_xres, pat9125_yres, pat9125_x, pat9125_y, pat9125_b, pat9125_s);
printf("x=%d y=%d b=%d s=%d\n", pat9125_x, pat9125_y, pat9125_b, pat9125_s);
return;
}
if (strchr_pointer[1+4] == '!')
{
pat9125_update();
printf("x=%d y=%d b=%d s=%d\n", pat9125_x, pat9125_y, pat9125_b, pat9125_s);
return;
}
/*
if (code_seen('R'))
{
unsigned char res = (int)code_value();
LOG("pat9125_init(xres=yres=%d)=%d\n", res, pat9125_init(res, res));
}
*/
if (code_seen('X'))
{
pat9125_x = (int)code_value();
LOG("pat9125_x=%d\n", pat9125_x);
}
if (code_seen('Y'))
{
pat9125_y = (int)code_value();
LOG("pat9125_y=%d\n", pat9125_y);
}
if (code_seen('L'))
{
fsensor_log = (int)code_value();
LOG("fsensor_log=%d\n", fsensor_log);
}
}
#endif //PAT9125
#endif //DEBUG_DCODES

28
Firmware/Dcodes.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef DCODES_H
#define DCODES_H
extern void dcode__1(); //D-1 - Endless loop (to simulate deadlock)
extern void dcode_0(); //D0 - Reset
extern void dcode_1(); //D1 - Clear EEPROM
extern void dcode_2(); //D2 - Read/Write RAM
extern void dcode_3(); //D3 - Read/Write EEPROM
extern void dcode_4(); //D4 - Read/Write PIN
extern void dcode_5(); //D5 - Read/Write FLASH
extern void dcode_6(); //D6 - Read/Write external FLASH
extern void dcode_7(); //D7 - Read/Write Bootloader
extern void dcode_8(); //D8 - Read/Write PINDA
extern void dcode_9(); //D9 - Read/Write ADC (Write=enable simulated, Read=disable simulated)
extern void dcode_10(); //D10 - XYZ calibration = OK
#ifdef TMC2130
extern void dcode_2130(); //D2130 - TMC2130
#endif //TMC2130
#ifdef PAT9125
extern void dcode_9125(); //D9125 - PAT9125
#endif //PAT9125
#endif //DCODES_H

35
Firmware/Firmware.ino Normal file
View File

@ -0,0 +1,35 @@
/* -*- c++ -*- */
/*
Reprap firmware based on Sprinter and grbl.
Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
This program 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 program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
This firmware is a mashup between Sprinter and grbl.
(https://github.com/kliment/Sprinter)
(https://github.com/simen/grbl/tree)
It has preliminary support for Matthew Roberts advance algorithm
http://reprap.org/pipermail/reprap-dev/2011-May/003323.html
*/
/* All the implementation is done in *.cpp files to get better compatibility with avr-gcc without the Arduino IDE */
/* Use this file to help the Arduino IDE find which Arduino libraries are needed and to keep documentation on GCode */
#include "Configuration.h"
#include "pins.h"

516
Firmware/Marlin.h Normal file
View File

@ -0,0 +1,516 @@
// Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware.
// License: GPL
#ifndef MARLIN_H
#define MARLIN_H
#define FORCE_INLINE __attribute__((always_inline)) inline
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include "system_timer.h"
#include "fastio.h"
#include "Configuration.h"
#include "pins.h"
#include "Timer.h"
extern uint8_t mbl_z_probe_nr;
#ifndef AT90USB
#define HardwareSerial_h // trick to disable the standard HWserial
#endif
#if (ARDUINO >= 100)
# include "Arduino.h"
#else
# include "WProgram.h"
#endif
// Arduino < 1.0.0 does not define this, so we need to do it ourselves
#ifndef analogInputToDigitalPin
# define analogInputToDigitalPin(p) ((p) + A0)
#endif
#ifdef AT90USB
#include "HardwareSerial.h"
#endif
#include "MarlinSerial.h"
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
//#include "WString.h"
#ifdef AT90USB
#ifdef BTENABLED
#define MYSERIAL bt
#else
#define MYSERIAL Serial
#endif // BTENABLED
#else
#define MYSERIAL MSerial
#endif
#include "lcd.h"
#ifdef __cplusplus
extern "C" {
#endif
extern FILE _uartout;
#ifdef __cplusplus
}
#endif
#define uartout (&_uartout)
#define SERIAL_PROTOCOL(x) (MYSERIAL.print(x))
#define SERIAL_PROTOCOL_F(x,y) (MYSERIAL.print(x,y))
#define SERIAL_PROTOCOLPGM(x) (serialprintPGM(PSTR(x)))
#define SERIAL_PROTOCOLRPGM(x) (serialprintPGM((x)))
#define SERIAL_PROTOCOLLN(x) (MYSERIAL.println(x)/*,MYSERIAL.write('\n')*/)
#define SERIAL_PROTOCOLLNPGM(x) (serialprintPGM(PSTR(x)),MYSERIAL.println()/*write('\n')*/)
#define SERIAL_PROTOCOLLNRPGM(x) (serialprintPGM((x)),MYSERIAL.println()/*write('\n')*/)
extern const char errormagic[] PROGMEM;
extern const char echomagic[] PROGMEM;
#define SERIAL_ERROR_START (serialprintPGM(errormagic))
#define SERIAL_ERROR(x) SERIAL_PROTOCOL(x)
#define SERIAL_ERRORPGM(x) SERIAL_PROTOCOLPGM(x)
#define SERIAL_ERRORRPGM(x) SERIAL_PROTOCOLRPGM(x)
#define SERIAL_ERRORLN(x) SERIAL_PROTOCOLLN(x)
#define SERIAL_ERRORLNPGM(x) SERIAL_PROTOCOLLNPGM(x)
#define SERIAL_ERRORLNRPGM(x) SERIAL_PROTOCOLLNRPGM(x)
#define SERIAL_ECHO_START (serialprintPGM(echomagic))
#define SERIAL_ECHO(x) SERIAL_PROTOCOL(x)
#define SERIAL_ECHOPGM(x) SERIAL_PROTOCOLPGM(x)
#define SERIAL_ECHORPGM(x) SERIAL_PROTOCOLRPGM(x)
#define SERIAL_ECHOLN(x) SERIAL_PROTOCOLLN(x)
#define SERIAL_ECHOLNPGM(x) SERIAL_PROTOCOLLNPGM(x)
#define SERIAL_ECHOLNRPGM(x) SERIAL_PROTOCOLLNRPGM(x)
#define SERIAL_ECHOPAIR(name,value) (serial_echopair_P(PSTR(name),(value)))
void serial_echopair_P(const char *s_P, float v);
void serial_echopair_P(const char *s_P, double v);
void serial_echopair_P(const char *s_P, unsigned long v);
//Things to write to serial from Program memory. Saves 400 to 2k of RAM.
// Making this FORCE_INLINE is not a good idea when running out of FLASH
// I'd rather skip a few CPU ticks than 5.5KB (!!) of FLASH
void serialprintPGM(const char *str);
bool is_buffer_empty();
void get_command();
void process_commands();
void ramming();
void manage_inactivity(bool ignore_stepper_queue=false);
#if defined(X_ENABLE_PIN) && X_ENABLE_PIN > -1
#define enable_x() WRITE(X_ENABLE_PIN, X_ENABLE_ON)
#define disable_x() { WRITE(X_ENABLE_PIN,!X_ENABLE_ON); axis_known_position[X_AXIS] = false; }
#else
#define enable_x() ;
#define disable_x() ;
#endif
#if defined(Y_ENABLE_PIN) && Y_ENABLE_PIN > -1
#ifdef Y_DUAL_STEPPER_DRIVERS
#define enable_y() { WRITE(Y_ENABLE_PIN, Y_ENABLE_ON); WRITE(Y2_ENABLE_PIN, Y_ENABLE_ON); }
#define disable_y() { WRITE(Y_ENABLE_PIN,!Y_ENABLE_ON); WRITE(Y2_ENABLE_PIN, !Y_ENABLE_ON); axis_known_position[Y_AXIS] = false; }
#else
#define enable_y() WRITE(Y_ENABLE_PIN, Y_ENABLE_ON)
#define disable_y() { WRITE(Y_ENABLE_PIN,!Y_ENABLE_ON); axis_known_position[Y_AXIS] = false; }
#endif
#else
#define enable_y() ;
#define disable_y() ;
#endif
#if defined(Z_ENABLE_PIN) && Z_ENABLE_PIN > -1
#if defined(Z_AXIS_ALWAYS_ON)
#ifdef Z_DUAL_STEPPER_DRIVERS
#define enable_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
#else
#define enable_z() WRITE(Z_ENABLE_PIN, Z_ENABLE_ON)
#define disable_z() {}
#endif
#else
#ifdef Z_DUAL_STEPPER_DRIVERS
#define enable_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
#else
#define enable_z() WRITE(Z_ENABLE_PIN, Z_ENABLE_ON)
#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
#endif
#endif
#else
#define enable_z() {}
#define disable_z() {}
#endif
#ifdef PSU_Delta
void init_force_z();
void check_force_z();
#undef disable_z
#define disable_z() disable_force_z()
void disable_force_z();
#undef enable_z
#define enable_z() enable_force_z()
void enable_force_z();
#endif // PSU_Delta
//#if defined(Z_ENABLE_PIN) && Z_ENABLE_PIN > -1
//#ifdef Z_DUAL_STEPPER_DRIVERS
//#define enable_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
//#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
//#else
//#define enable_z() WRITE(Z_ENABLE_PIN, Z_ENABLE_ON)
//#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
//#endif
//#else
//#define enable_z() ;
//#define disable_z() ;
//#endif
#if defined(E0_ENABLE_PIN) && (E0_ENABLE_PIN > -1)
#define enable_e0() WRITE(E0_ENABLE_PIN, E_ENABLE_ON)
#define disable_e0() WRITE(E0_ENABLE_PIN,!E_ENABLE_ON)
#else
#define enable_e0() /* nothing */
#define disable_e0() /* nothing */
#endif
#if (EXTRUDERS > 1) && defined(E1_ENABLE_PIN) && (E1_ENABLE_PIN > -1)
#define enable_e1() WRITE(E1_ENABLE_PIN, E_ENABLE_ON)
#define disable_e1() WRITE(E1_ENABLE_PIN,!E_ENABLE_ON)
#else
#define enable_e1() /* nothing */
#define disable_e1() /* nothing */
#endif
#if (EXTRUDERS > 2) && defined(E2_ENABLE_PIN) && (E2_ENABLE_PIN > -1)
#define enable_e2() WRITE(E2_ENABLE_PIN, E_ENABLE_ON)
#define disable_e2() WRITE(E2_ENABLE_PIN,!E_ENABLE_ON)
#else
#define enable_e2() /* nothing */
#define disable_e2() /* nothing */
#endif
#define FARM_FILAMENT_COLOR_NONE 99;
enum AxisEnum {X_AXIS=0, Y_AXIS=1, Z_AXIS=2, E_AXIS=3, X_HEAD=4, Y_HEAD=5};
#define X_AXIS_MASK 1
#define Y_AXIS_MASK 2
#define Z_AXIS_MASK 4
#define E_AXIS_MASK 8
#define X_HEAD_MASK 16
#define Y_HEAD_MASK 32
void FlushSerialRequestResend();
void ClearToSend();
void update_currents();
void get_coordinates();
void prepare_move();
void kill(const char *full_screen_message = NULL, unsigned char id = 0);
void Stop();
bool IsStopped();
//put an ASCII command at the end of the current buffer.
void enquecommand(const char *cmd, bool from_progmem = false);
//put an ASCII command at the end of the current buffer, read from flash
#define enquecommand_P(cmd) enquecommand(cmd, true)
//put an ASCII command at the begin of the current buffer
void enquecommand_front(const char *cmd, bool from_progmem = false);
//put an ASCII command at the begin of the current buffer, read from flash
#define enquecommand_front_P(cmd) enquecommand_front(cmd, true)
void repeatcommand_front();
// Remove all lines from the command queue.
void cmdqueue_reset();
void prepare_arc_move(char isclockwise);
void clamp_to_software_endstops(float target[3]);
void refresh_cmd_timeout(void);
// Timer counter, incremented by the 1ms Arduino timer.
// The standard Arduino timer() function returns this value atomically
// by disabling / enabling interrupts. This is costly, if the interrupts are known
// to be disabled.
#ifdef SYSTEM_TIMER_2
extern volatile unsigned long timer2_millis;
#else //SYSTEM_TIMER_2
extern volatile unsigned long timer0_millis;
#endif //SYSTEM_TIMER_2
// An unsynchronized equivalent to a standard Arduino _millis() function.
// To be used inside an interrupt routine.
FORCE_INLINE unsigned long millis_nc() {
#ifdef SYSTEM_TIMER_2
return timer2_millis;
#else //SYSTEM_TIMER_2
return timer0_millis;
#endif //SYSTEM_TIMER_2
}
#ifdef FAST_PWM_FAN
void setPwmFrequency(uint8_t pin, int val);
#endif
#ifndef CRITICAL_SECTION_START
#define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli();
#define CRITICAL_SECTION_END SREG = _sreg;
#endif //CRITICAL_SECTION_START
extern bool fans_check_enabled;
extern float homing_feedrate[];
extern bool axis_relative_modes[];
extern int feedmultiply;
extern int extrudemultiply; // Sets extrude multiply factor (in percent) for all extruders
extern int extruder_multiply[EXTRUDERS]; // sets extrude multiply factor (in percent) for each extruder individually
extern float volumetric_multiplier[EXTRUDERS]; // reciprocal of cross-sectional area of filament (in square millimeters), stored this way to reduce computational burden in planner
extern float current_position[NUM_AXIS] ;
extern float destination[NUM_AXIS] ;
extern float min_pos[3];
extern float max_pos[3];
extern bool axis_known_position[3];
extern int fanSpeed;
extern int8_t lcd_change_fil_state;
#ifdef TMC2130
void homeaxis(int axis, uint8_t cnt = 1, uint8_t* pstep = 0);
#else
void homeaxis(int axis, uint8_t cnt = 1);
#endif //TMC2130
#ifdef FAN_SOFT_PWM
extern unsigned char fanSpeedSoftPwm;
#endif
#ifdef FWRETRACT
extern bool retracted[EXTRUDERS];
extern float retract_length_swap;
extern float retract_recover_length_swap;
#endif
extern uint8_t host_keepalive_interval;
extern unsigned long starttime;
extern unsigned long stoptime;
extern int bowden_length[4];
extern bool is_usb_printing;
extern bool homing_flag;
extern bool temp_cal_active;
extern bool loading_flag;
extern unsigned int usb_printing_counter;
extern unsigned long kicktime;
extern unsigned long total_filament_used;
void save_statistics(unsigned long _total_filament_used, unsigned long _total_print_time);
extern unsigned int heating_status;
extern unsigned int status_number;
extern unsigned int heating_status_counter;
extern char snmm_filaments_used;
extern unsigned long PingTime;
extern unsigned long NcTime;
extern bool no_response;
extern uint8_t important_status;
extern uint8_t saved_filament_type;
extern bool fan_state[2];
extern int fan_edge_counter[2];
extern int fan_speed[2];
// Handling multiple extruders pins
extern uint8_t active_extruder;
#endif
//Long pause
extern unsigned long pause_time;
extern unsigned long start_pause_print;
extern unsigned long t_fan_rising_edge;
extern bool mesh_bed_leveling_flag;
extern bool mesh_bed_run_from_menu;
extern bool sortAlpha;
extern char dir_names[3][9];
extern int8_t lcd_change_fil_state;
// save/restore printing
extern bool saved_printing;
extern uint8_t saved_printing_type;
#define PRINTING_TYPE_SD 0
#define PRINTING_TYPE_USB 1
#define PRINTING_TYPE_NONE 2
//save/restore printing in case that mmu is not responding
extern bool mmu_print_saved;
//estimated time to end of the print
extern uint8_t print_percent_done_normal;
extern uint16_t print_time_remaining_normal;
extern uint8_t print_percent_done_silent;
extern uint16_t print_time_remaining_silent;
#define PRINT_TIME_REMAINING_INIT 0xffff
extern uint16_t mcode_in_progress;
extern uint16_t gcode_in_progress;
extern LongTimer safetyTimer;
#define PRINT_PERCENT_DONE_INIT 0xff
#define PRINTER_ACTIVE (IS_SD_PRINTING || is_usb_printing || isPrintPaused || (custom_message_type == CustomMsg::TempCal) || saved_printing || (lcd_commands_type == LcdCommands::Layer1Cal) || card.paused || mmu_print_saved)
//! Beware - mcode_in_progress is set as soon as the command gets really processed,
//! which is not the same as posting the M600 command into the command queue
//! There can be a considerable lag between posting M600 and its real processing which might result
//! in posting multiple M600's into the command queue
//! Instead, the fsensor uses another state variable :( , which is set to true, when the M600 command is enqued
//! and is reset to false when the fsensor returns into its filament runout finished handler
//! I'd normally change this macro, but who knows what would happen in the MMU :)
#define CHECK_FSENSOR ((IS_SD_PRINTING || is_usb_printing) && (mcode_in_progress != 600) && !saved_printing && e_active())
extern void calculate_extruder_multipliers();
// Similar to the default Arduino delay function,
// but it keeps the background tasks running.
extern void delay_keep_alive(unsigned int ms);
extern void check_babystep();
extern void long_pause();
extern void crashdet_stop_and_save_print();
#ifdef HEATBED_ANALYSIS
void d_setup();
float d_ReadData();
void bed_analysis(float x_dimension, float y_dimension, int x_points_num, int y_points_num, float shift_x, float shift_y);
void bed_check(float x_dimension, float y_dimension, int x_points_num, int y_points_num, float shift_x, float shift_y);
#endif //HEATBED_ANALYSIS
float temp_comp_interpolation(float temperature);
void show_fw_version_warnings();
uint8_t check_printer_version();
#ifdef PINDA_THERMISTOR
float temp_compensation_pinda_thermistor_offset(float temperature_pinda);
#endif //PINDA_THERMISTOR
void serialecho_temperatures();
bool check_commands();
void uvlo_();
void uvlo_tiny();
void recover_print(uint8_t automatic);
void setup_uvlo_interrupt();
#if defined(TACH_1) && TACH_1 >-1
void setup_fan_interrupt();
#endif
//extern void recover_machine_state_after_power_panic();
extern void recover_machine_state_after_power_panic(bool bTiny);
extern void restore_print_from_eeprom();
extern void position_menu();
extern void print_world_coordinates();
extern void print_physical_coordinates();
extern void print_mesh_bed_leveling_table();
extern void stop_and_save_print_to_ram(float z_move, float e_move);
extern void restore_print_from_ram_and_continue(float e_move);
//estimated time to end of the print
extern uint16_t print_time_remaining();
extern uint8_t calc_percent_done();
// States for managing Marlin and host communication
// Marlin sends messages if blocked or busy
/*enum MarlinBusyState {
NOT_BUSY, // Not in a handler
IN_HANDLER, // Processing a GCode
IN_PROCESS, // Known to be blocking command input (as in G29)
PAUSED_FOR_USER, // Blocking pending any input
PAUSED_FOR_INPUT // Blocking pending text input (concept)
};*/
#define NOT_BUSY 1
#define IN_HANDLER 2
#define IN_PROCESS 3
#define PAUSED_FOR_USER 4
#define PAUSED_FOR_INPUT 5
#define KEEPALIVE_STATE(n) do { busy_state = n;} while (0)
extern void host_keepalive();
//extern MarlinBusyState busy_state;
extern int8_t busy_state;
#ifdef TMC2130
#define FORCE_HIGH_POWER_START force_high_power_mode(true)
#define FORCE_HIGH_POWER_END force_high_power_mode(false)
void force_high_power_mode(bool start_high_power_section);
#endif //TMC2130
// G-codes
bool gcode_M45(bool onlyZ, int8_t verbosity_level);
void gcode_M114();
void gcode_M701();
#define UVLO !(PINE & (1<<4))
void proc_commands();
void M600_load_filament();
void M600_load_filament_movements();
void M600_wait_for_user(float HotendTempBckp);
void M600_check_state(float nozzle_temp);
void load_filament_final_feed();
void marlin_wait_for_click();
void marlin_rise_z(void);

385
Firmware/MarlinSerial.cpp Normal file
View File

@ -0,0 +1,385 @@
/*
HardwareSerial.cpp - 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 23 November 2006 by David A. Mellis
Modified 28 September 2010 by Mark Sproul
*/
#include "Marlin.h"
#include "MarlinSerial.h"
uint8_t selectedSerialPort = 0;
#ifndef AT90USB
// this next line disables the entire HardwareSerial.cpp,
// this is so I can support Attiny series and any other chip without a UART
#if defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H)
#if UART_PRESENT(SERIAL_PORT)
ring_buffer rx_buffer = { { 0 }, 0, 0 };
#endif
FORCE_INLINE void store_char(unsigned char c)
{
int i = (unsigned int)(rx_buffer.head + 1) % RX_BUFFER_SIZE;
// 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 != rx_buffer.tail) {
rx_buffer.buffer[rx_buffer.head] = c;
rx_buffer.head = i;
}
}
#if defined(M_USARTx_RX_vect)
// The serial line receive interrupt routine for a baud rate 115200
// ticks at maximum 11.76 kHz and blocks for 2.688 us at each tick.
// If the serial line is fully utilized, this corresponds to 3.16%
// loading of the CPU (the interrupt invocation overhead not taken into account).
// As the serial line is not fully utilized, the CPU load is likely around 1%.
ISR(M_USARTx_RX_vect)
{
// Test for a framing error.
if (M_UCSRxA & (1<<M_FEx))
{
// Characters received with the framing errors will be ignored.
// Dummy register read (discard)
(void)(*(char *)M_UDRx);
}
else
{
// Read the input register.
unsigned char c = M_UDRx;
if (selectedSerialPort == 0)
store_char(c);
#ifdef DEBUG_DUMP_TO_2ND_SERIAL
UDR1 = c;
#endif //DEBUG_DUMP_TO_2ND_SERIAL
}
}
#ifndef SNMM
ISR(USART1_RX_vect)
{
// Test for a framing error.
if (UCSR1A & (1<<FE1))
{
// Characters received with the framing errors will be ignored.
// Dummy register read (discard)
(void)(*(char *)UDR1);
}
else
{
// Read the input register.
unsigned char c = UDR1;
if (selectedSerialPort == 1)
store_char(c);
#ifdef DEBUG_DUMP_TO_2ND_SERIAL
M_UDRx = c;
#endif //DEBUG_DUMP_TO_2ND_SERIAL
}
}
#endif
#endif
// Public Methods //////////////////////////////////////////////////////////////
void MarlinSerial::begin(long baud)
{
uint16_t baud_setting;
bool useU2X = true;
#if F_CPU == 16000000UL && SERIAL_PORT == 0
// hard-coded 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) {
useU2X = false;
}
#endif
// set up the first (original serial port)
if (useU2X) {
M_UCSRxA = 1 << M_U2Xx;
baud_setting = (F_CPU / 4 / baud - 1) / 2;
} else {
M_UCSRxA = 0;
baud_setting = (F_CPU / 8 / baud - 1) / 2;
}
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
M_UBRRxH = baud_setting >> 8;
M_UBRRxL = baud_setting;
sbi(M_UCSRxB, M_RXENx);
sbi(M_UCSRxB, M_TXENx);
sbi(M_UCSRxB, M_RXCIEx);
#ifndef SNMM
if (selectedSerialPort == 1) { //set up also the second serial port
if (useU2X) {
UCSR1A = 1 << U2X1;
baud_setting = (F_CPU / 4 / baud - 1) / 2;
} else {
UCSR1A = 0;
baud_setting = (F_CPU / 8 / baud - 1) / 2;
}
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
UBRR1H = baud_setting >> 8;
UBRR1L = baud_setting;
sbi(UCSR1B, RXEN1);
sbi(UCSR1B, TXEN1);
sbi(UCSR1B, RXCIE1);
}
#endif
}
void MarlinSerial::end()
{
cbi(M_UCSRxB, M_RXENx);
cbi(M_UCSRxB, M_TXENx);
cbi(M_UCSRxB, M_RXCIEx);
#ifndef SNMM
cbi(UCSR1B, RXEN1);
cbi(UCSR1B, TXEN1);
cbi(UCSR1B, RXCIE1);
#endif
}
int MarlinSerial::peek(void)
{
if (rx_buffer.head == rx_buffer.tail) {
return -1;
} else {
return rx_buffer.buffer[rx_buffer.tail];
}
}
int MarlinSerial::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) % RX_BUFFER_SIZE;
return c;
}
}
void MarlinSerial::flush()
{
// don't reverse this or there may be problems if the RX interrupt
// occurs after reading the value of rx_buffer_head but before writing
// the value to rx_buffer_tail; the previous value of rx_buffer_head
// may be written to rx_buffer_tail, making it appear as if the buffer
// were full, not empty.
rx_buffer.head = rx_buffer.tail;
}
/// imports from print.h
void MarlinSerial::print(char c, int base)
{
print((long) c, base);
}
void MarlinSerial::print(unsigned char b, int base)
{
print((unsigned long) b, base);
}
void MarlinSerial::print(int n, int base)
{
print((long) n, base);
}
void MarlinSerial::print(unsigned int n, int base)
{
print((unsigned long) n, base);
}
void MarlinSerial::print(long n, int base)
{
if (base == 0) {
write(n);
} else if (base == 10) {
if (n < 0) {
print('-');
n = -n;
}
printNumber(n, 10);
} else {
printNumber(n, base);
}
}
void MarlinSerial::print(unsigned long n, int base)
{
if (base == 0) write(n);
else printNumber(n, base);
}
void MarlinSerial::print(double n, int digits)
{
printFloat(n, digits);
}
void MarlinSerial::println(void)
{
print('\r');
print('\n');
}
/*void MarlinSerial::println(const String &s)
{
print(s);
println();
}*/
void MarlinSerial::println(const char c[])
{
print(c);
println();
}
void MarlinSerial::println(char c, int base)
{
print(c, base);
println();
}
void MarlinSerial::println(unsigned char b, int base)
{
print(b, base);
println();
}
void MarlinSerial::println(int n, int base)
{
print(n, base);
println();
}
void MarlinSerial::println(unsigned int n, int base)
{
print(n, base);
println();
}
void MarlinSerial::println(long n, int base)
{
print(n, base);
println();
}
void MarlinSerial::println(unsigned long n, int base)
{
print(n, base);
println();
}
void MarlinSerial::println(double n, int digits)
{
print(n, digits);
println();
}
// Private Methods /////////////////////////////////////////////////////////////
void MarlinSerial::printNumber(unsigned long n, uint8_t base)
{
unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
unsigned long i = 0;
if (n == 0) {
print('0');
return;
}
while (n > 0) {
buf[i++] = n % base;
n /= base;
}
for (; i > 0; i--)
print((char) (buf[i - 1] < 10 ?
'0' + buf[i - 1] :
'A' + buf[i - 1] - 10));
}
void MarlinSerial::printFloat(double number, uint8_t digits)
{
// Handle negative numbers
if (number < 0.0)
{
print('-');
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i=0; i<digits; ++i)
rounding /= 10.0;
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
print(int_part);
// Print the decimal point, but only if there are digits beyond
if (digits > 0)
print(".");
// Extract digits from the remainder one at a time
while (digits-- > 0)
{
remainder *= 10.0;
int toPrint = int(remainder);
print(toPrint);
remainder -= toPrint;
}
}
// Preinstantiate Objects //////////////////////////////////////////////////////
MarlinSerial MSerial;
#endif // whole file
#endif // !AT90USB
// For AT90USB targets use the UART for BT interfacing
#if defined(AT90USB) && defined (BTENABLED)
HardwareSerial bt;
#endif

239
Firmware/MarlinSerial.h Normal file
View File

@ -0,0 +1,239 @@
/*
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
*/
#ifndef MarlinSerial_h
#define MarlinSerial_h
#include "Marlin.h"
#if !defined(SERIAL_PORT)
#define SERIAL_PORT 0
#endif
// The presence of the UBRRH register is used to detect a UART.
#define UART_PRESENT(port) ((port == 0 && (defined(UBRRH) || defined(UBRR0H))) || \
(port == 1 && defined(UBRR1H)) || (port == 2 && defined(UBRR2H)) || \
(port == 3 && defined(UBRR3H)))
// These are macros to build serial port register names for the selected SERIAL_PORT (C preprocessor
// requires two levels of indirection to expand macro values properly)
#define SERIAL_REGNAME(registerbase,number,suffix) SERIAL_REGNAME_INTERNAL(registerbase,number,suffix)
#if SERIAL_PORT == 0 && (!defined(UBRR0H) || !defined(UDR0)) // use un-numbered registers if necessary
#define SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) registerbase##suffix
#else
#define SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) registerbase##number##suffix
#endif
// Registers used by MarlinSerial class (these are expanded
// depending on selected serial port
#define M_UCSRxA SERIAL_REGNAME(UCSR,SERIAL_PORT,A) // defines M_UCSRxA to be UCSRnA where n is the serial port number
#define M_UCSRxB SERIAL_REGNAME(UCSR,SERIAL_PORT,B)
#define M_RXENx SERIAL_REGNAME(RXEN,SERIAL_PORT,)
#define M_TXENx SERIAL_REGNAME(TXEN,SERIAL_PORT,)
#define M_RXCIEx SERIAL_REGNAME(RXCIE,SERIAL_PORT,)
#define M_UDREx SERIAL_REGNAME(UDRE,SERIAL_PORT,)
#define M_UDRx SERIAL_REGNAME(UDR,SERIAL_PORT,)
#define M_UBRRxH SERIAL_REGNAME(UBRR,SERIAL_PORT,H)
#define M_UBRRxL SERIAL_REGNAME(UBRR,SERIAL_PORT,L)
#define M_RXCx SERIAL_REGNAME(RXC,SERIAL_PORT,)
#define M_FEx SERIAL_REGNAME(FE,SERIAL_PORT,)
#define M_USARTx_RX_vect SERIAL_REGNAME(USART,SERIAL_PORT,_RX_vect)
#define M_U2Xx SERIAL_REGNAME(U2X,SERIAL_PORT,)
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
#define BYTE 0
#ifndef AT90USB
// Define constants and variables for buffering incoming serial data. We're
// using a ring buffer (I think), in which rx_buffer_head is the index of the
// location to which to write the next incoming character and rx_buffer_tail
// is the index of the location from which to read.
#define RX_BUFFER_SIZE 128
extern uint8_t selectedSerialPort;
struct ring_buffer
{
unsigned char buffer[RX_BUFFER_SIZE];
int head;
int tail;
};
#if UART_PRESENT(SERIAL_PORT)
extern ring_buffer rx_buffer;
#endif
class MarlinSerial //: public Stream
{
public:
static void begin(long);
static void end();
static int peek(void);
static int read(void);
static void flush(void);
static /*FORCE_INLINE*/ int available(void)
{
return (unsigned int)(RX_BUFFER_SIZE + rx_buffer.head - rx_buffer.tail) % RX_BUFFER_SIZE;
}
/*
FORCE_INLINE void write(uint8_t c)
{
while (!((M_UCSRxA) & (1 << M_UDREx)))
;
M_UDRx = c;
}
*/
static void write(uint8_t c)
{
if (selectedSerialPort == 0)
{
while (!((M_UCSRxA) & (1 << M_UDREx)));
M_UDRx = c;
}
else if (selectedSerialPort == 1)
{
while (!((UCSR1A) & (1 << UDRE1)));
UDR1 = c;
}
}
static void checkRx(void)
{
if (selectedSerialPort == 0) {
if((M_UCSRxA & (1<<M_RXCx)) != 0) {
// Test for a framing error.
if (M_UCSRxA & (1<<M_FEx)) {
// Characters received with the framing errors will be ignored.
// The temporary variable "c" was made volatile, so the compiler does not optimize this out.
(void)(*(char *)M_UDRx);
} else {
unsigned char c = M_UDRx;
int i = (unsigned int)(rx_buffer.head + 1) % RX_BUFFER_SIZE;
// 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 != rx_buffer.tail) {
rx_buffer.buffer[rx_buffer.head] = c;
rx_buffer.head = i;
}
//selectedSerialPort = 0;
#ifdef DEBUG_DUMP_TO_2ND_SERIAL
UDR1 = c;
#endif //DEBUG_DUMP_TO_2ND_SERIAL
}
}
} else { // if(selectedSerialPort == 1) {
if((UCSR1A & (1<<RXC1)) != 0) {
// Test for a framing error.
if (UCSR1A & (1<<FE1)) {
// Characters received with the framing errors will be ignored.
// The temporary variable "c" was made volatile, so the compiler does not optimize this out.
(void)(*(char *)UDR1);
} else {
unsigned char c = UDR1;
int i = (unsigned int)(rx_buffer.head + 1) % RX_BUFFER_SIZE;
// 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 != rx_buffer.tail) {
rx_buffer.buffer[rx_buffer.head] = c;
rx_buffer.head = i;
}
//selectedSerialPort = 1;
#ifdef DEBUG_DUMP_TO_2ND_SERIAL
M_UDRx = c;
#endif //DEBUG_DUMP_TO_2ND_SERIAL
}
}
}
}
private:
static void printNumber(unsigned long, uint8_t);
static void printFloat(double, uint8_t);
public:
static /*FORCE_INLINE*/ void write(const char *str)
{
while (*str)
write(*str++);
}
static /*FORCE_INLINE*/ void write(const uint8_t *buffer, size_t size)
{
while (size--)
write(*buffer++);
}
/* static FORCE_INLINE void print(const String &s)
{
for (int i = 0; i < (int)s.length(); i++) {
write(s[i]);
}
}*/
static FORCE_INLINE void print(const char *str)
{
write(str);
}
static void print(char, int = BYTE);
static void print(unsigned char, int = BYTE);
static void print(int, int = DEC);
static void print(unsigned int, int = DEC);
static void print(long, int = DEC);
static void print(unsigned long, int = DEC);
static void print(double, int = 2);
// static void println(const String &s);
static void println(const char[]);
static void println(char, int = BYTE);
static void println(unsigned char, int = BYTE);
static void println(int, int = DEC);
static void println(unsigned int, int = DEC);
static void println(long, int = DEC);
static void println(unsigned long, int = DEC);
static void println(double, int = 2);
static void println(void);
};
extern MarlinSerial MSerial;
#endif // !AT90USB
// Use the UART for BT in AT90USB configurations
#if defined(AT90USB) && defined (BTENABLED)
extern HardwareSerial bt;
#endif
#endif

10573
Firmware/Marlin_main.cpp Normal file

File diff suppressed because it is too large Load Diff

813
Firmware/Sd2Card.cpp Normal file
View File

@ -0,0 +1,813 @@
/* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card 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 Sd2Card Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#include "Sd2Card.h"
//------------------------------------------------------------------------------
#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 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))) { /* Intentionally left empty */ }
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))) { /* Intentionally left empty */ }
buf[i] = SPDR;
SPDR = 0XFF;
}
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
buf[nbyte] = SPDR;
}
//------------------------------------------------------------------------------
/** SPI send a byte */
static void spiSend(uint8_t b) {
SPDR = b;
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
}
//------------------------------------------------------------------------------
/** 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))) { /* Intentionally left empty */ }
SPDR = buf[i];
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
SPDR = buf[i + 1];
}
while (!(SPSR & (1 << SPIF))) { /* Intentionally left empty */ }
}
//------------------------------------------------------------------------------
#else // SOFTWARE_SPI
//------------------------------------------------------------------------------
/** nop to tune soft SPI timing */
#define nop asm volatile ("nop\n\t")
//------------------------------------------------------------------------------
/** Soft SPI receive byte */
static uint8_t spiRec() {
uint8_t data = 0;
// no interrupts during byte receive - about 8 us
cli();
// output pin high - like sending 0XFF
fastDigitalWrite(SPI_MOSI_PIN, HIGH);
for (uint8_t i = 0; i < 8; i++) {
fastDigitalWrite(SPI_SCK_PIN, HIGH);
// adjust so SCK is nice
nop;
nop;
data <<= 1;
if (fastDigitalRead(SPI_MISO_PIN)) data |= 1;
fastDigitalWrite(SPI_SCK_PIN, LOW);
}
// enable interrupts
sei();
return data;
}
//------------------------------------------------------------------------------
/** 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) {
// no interrupts during byte send - about 8 us
cli();
for (uint8_t i = 0; i < 8; i++) {
fastDigitalWrite(SPI_SCK_PIN, LOW);
fastDigitalWrite(SPI_MOSI_PIN, data & 0X80);
data <<= 1;
fastDigitalWrite(SPI_SCK_PIN, HIGH);
}
// hold SCK high for a few ns
nop;
nop;
nop;
nop;
fastDigitalWrite(SPI_SCK_PIN, LOW);
// enable interrupts
sei();
}
//------------------------------------------------------------------------------
/** Soft SPI send block */
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
//------------------------------------------------------------------------------
// 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);
// send command
spiSend(cmd | 0x40);
// send argument
for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s);
// send CRC
uint8_t crc = 0XFF;
if (cmd == CMD0) crc = 0X95; // correct crc for CMD0 with arg 0
if (cmd == CMD8) crc = 0X87; // correct crc for CMD8 with arg 0X1AA
spiSend(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++) { /* Intentionally left empty */ }
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 = ((uint32_t)csd.v2.c_size_high << 16)
| (csd.v2.c_size_mid << 8) | 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);
}
//------------------------------------------------------------------------------
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;
// set pin modes
pinMode(chipSelectPin_, OUTPUT);
chipSelectHigh();
pinMode(SPI_MISO_PIN, INPUT);
pinMode(SPI_MOSI_PIN, OUTPUT);
pinMode(SPI_SCK_PIN, OUTPUT);
#ifndef SOFTWARE_SPI
// SS must be in output mode even it is not chip select
pinMode(SS_PIN, OUTPUT);
// set SS high - may be chip select for another SPI device
#if SET_SPI_SS_HIGH
digitalWrite(SS_PIN, HIGH);
#endif // SET_SPI_SS_HIGH
// 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 ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
if (((uint16_t)_millis() - t0) > SD_INIT_TIMEOUT) {
error(SD_CARD_ERROR_CMD0);
goto fail;
}
}
// 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 ((status_ = 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) {
#ifdef SD_CHECK_AND_RETRY
uint8_t retryCnt = 3;
// use address if not SDHC card
if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
retry2:
retryCnt --;
if (cardCommand(CMD17, blockNumber)) {
error(SD_CARD_ERROR_CMD17);
if (retryCnt > 0) goto retry;
goto fail;
}
if (!readData(dst, 512))
{
if (retryCnt > 0) goto retry;
goto fail;
}
return true;
retry:
chipSelectHigh();
cardCommand(CMD12, 0);//Try sending a stop command, but ignore the result.
errorCode_ = 0;
goto retry2;
#else
// 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);
#endif
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);
}
#ifdef SD_CHECK_AND_RETRY
static const 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, size_t n) {
uint16_t crc = 0;
for (size_t i = 0; i < n; i++) {
crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0XFF]) ^ (crc << 8);
}
return crc;
}
#endif
//------------------------------------------------------------------------------
bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
// 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);
#ifdef SD_CHECK_AND_RETRY
{
uint16_t calcCrc = CRC_CCITT(dst, count);
uint16_t recvCrc = spiRec() << 8;
recvCrc |= spiRec();
if (calcCrc != recvCrc)
{
error(SD_CARD_ERROR_CRC);
goto fail;
}
}
#else
// discard CRC
spiRec();
spiRec();
#endif
chipSelectHigh();
// Toshiba FlashAir Patch. Purge pending status byte.
if (flash_air_compatible_)
spiSend(0XFF);
return true;
fail:
chipSelectHigh();
// Toshiba FlashAir Patch. Purge pending status byte.
if (flash_air_compatible_)
spiSend(0XFF);
return false;
}
//------------------------------------------------------------------------------
/** read CID or CSR register */
bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(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) {
spiSendBlock(token, src);
spiSend(0xff); // dummy crc
spiSend(0xff); // dummy crc
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;
}
//------------------------------------------------------------------------------
/** Wait for start block token */
//FIXME Vojtech: Copied from a current version of Sd2Card Arduino code.
// We shall likely upgrade the rest of the Sd2Card.
uint8_t Sd2Card::waitStartBlock(void) {
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;
}
return true;
fail:
chipSelectHigh();
return false;
}
// Toshiba FlashAir support, copied from
// https://flashair-developers.com/en/documents/tutorials/arduino/
//------------------------------------------------------------------------------
/** Perform Extention Read. */
uint8_t Sd2Card::readExt(uint32_t arg, uint8_t* dst, uint16_t count) {
uint16_t i;
// send command and argument.
if (cardCommand(CMD48, arg)) {
error(SD_CARD_ERROR_CMD48);
goto fail;
}
// wait for start block token.
if (!waitStartBlock()) {
goto fail;
}
// receive data
for (i = 0; i < count; ++i) {
dst[i] = spiRec();
}
// skip dummy bytes and 16-bit crc.
for (; i < 514; ++i) {
spiRec();
}
chipSelectHigh();
spiSend(0xFF); // dummy clock to force FlashAir finish the command.
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
* Read an extension register space.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::readExtMemory(uint8_t mio, uint8_t func,
uint32_t addr, uint16_t count, uint8_t* dst) {
uint32_t offset = addr & 0x1FF;
if (offset + count > 512) count = 512 - offset;
if (count == 0) return true;
uint32_t arg =
(((uint32_t)mio & 0x1) << 31) |
(mio ? (((uint32_t)func & 0x7) << 28) : (((uint32_t)func & 0xF) << 27)) |
((addr & 0x1FFFF) << 9) |
((count - 1) & 0x1FF);
return readExt(arg, dst, count);
}
#endif

262
Firmware/Sd2Card.h Normal file
View File

@ -0,0 +1,262 @@
/* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card 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 Sd2Card Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef Sd2Card_h
#define Sd2Card_h
/**
* \file
* \brief Sd2Card class for V2 SD/SDHC cards
*/
#include "SdFatConfig.h"
#include "Sd2PinMap.h"
#include "SdInfo.h"
//------------------------------------------------------------------------------
// 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;
/** crc check error */
uint8_t const SD_CARD_ERROR_CRC = 0X20;
/** Toshiba FlashAir: iSDIO */
uint8_t const SD_CARD_ERROR_CMD48 = 0x80;
/** Toshiba FlashAir: iSDIO */
uint8_t const SD_CARD_ERROR_CMD49 = 0x81;
//------------------------------------------------------------------------------
// 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 MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__))
#define SOFTWARE_SPI
#elif USE_SOFTWARE_SPI
#define SOFTWARE_SPI
#endif // MEGA_SOFT_SPI
//------------------------------------------------------------------------------
// SPI pin definitions - do not edit here - change in SdFatConfig.h
//
#ifndef SOFTWARE_SPI
// hardware pin defs
/** The default chip select pin for the SD card is SS. */
uint8_t const SD_CHIP_SELECT_PIN = SS_PIN;
// The following three pins must not be redefined for hardware SPI.
/** SPI Master Out Slave In pin */
uint8_t const SPI_MOSI_PIN = MOSI_PIN;
/** SPI Master In Slave Out pin */
uint8_t const SPI_MISO_PIN = MISO_PIN;
/** SPI Clock pin */
uint8_t const SPI_SCK_PIN = SCK_PIN;
#else // SOFTWARE_SPI
/** SPI chip select pin */
uint8_t const SD_CHIP_SELECT_PIN = SOFT_SPI_CS_PIN;
/** SPI Master Out Slave In pin */
uint8_t const SPI_MOSI_PIN = SOFT_SPI_MOSI_PIN;
/** SPI Master In Slave Out pin */
uint8_t const SPI_MISO_PIN = SOFT_SPI_MISO_PIN;
/** SPI Clock pin */
uint8_t const SPI_SCK_PIN = SOFT_SPI_SCK_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), flash_air_compatible_(false) {}
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();
// Toshiba FlashAir support
uint8_t readExtMemory(uint8_t mio, uint8_t func, uint32_t addr, uint16_t count, uint8_t* dst);
void setFlashAirCompatible(bool flashAirCompatible) { flash_air_compatible_ = flashAirCompatible; }
bool getFlashAirCompatible() const { return flash_air_compatible_; }
private:
//----------------------------------------------------------------------------
uint8_t chipSelectPin_;
uint8_t errorCode_;
uint8_t spiRate_;
uint8_t status_;
uint8_t type_;
bool flash_air_compatible_;
// 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);
// Toshiba FlashAir support
uint8_t waitStartBlock(void);
uint8_t readExt(uint32_t arg, uint8_t* dst, uint16_t count);
};
#endif // Sd2Card_h
#endif

368
Firmware/Sd2PinMap.h Normal file
View File

@ -0,0 +1,368 @@
/* Arduino SdFat Library
* Copyright (C) 2010 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
* <http://www.gnu.org/licenses/>.
*/
// Warning this file was generated by a program.
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef Sd2PinMap_h
#define Sd2PinMap_h
#include <avr/io.h>
//------------------------------------------------------------------------------
/** struct for mapping digital pins */
struct pin_map_t {
volatile uint8_t* ddr;
volatile uint8_t* pin;
volatile uint8_t* port;
uint8_t bit;
};
//------------------------------------------------------------------------------
#if defined(__AVR_ATmega1280__)\
|| defined(__AVR_ATmega2560__)
// Mega
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 20; // D1
uint8_t const SCL_PIN = 21; // D0
#undef MOSI_PIN
#undef MISO_PIN
// SPI port
uint8_t const SS_PIN = 53; // B0
uint8_t const MOSI_PIN = 51; // B2
uint8_t const MISO_PIN = 50; // B3
uint8_t const SCK_PIN = 52; // B1
static const pin_map_t digitalPinMap[] = {
{&DDRE, &PINE, &PORTE, 0}, // E0 0
{&DDRE, &PINE, &PORTE, 1}, // E1 1
{&DDRE, &PINE, &PORTE, 4}, // E4 2
{&DDRE, &PINE, &PORTE, 5}, // E5 3
{&DDRG, &PING, &PORTG, 5}, // G5 4
{&DDRE, &PINE, &PORTE, 3}, // E3 5
{&DDRH, &PINH, &PORTH, 3}, // H3 6
{&DDRH, &PINH, &PORTH, 4}, // H4 7
{&DDRH, &PINH, &PORTH, 5}, // H5 8
{&DDRH, &PINH, &PORTH, 6}, // H6 9
{&DDRB, &PINB, &PORTB, 4}, // B4 10
{&DDRB, &PINB, &PORTB, 5}, // B5 11
{&DDRB, &PINB, &PORTB, 6}, // B6 12
{&DDRB, &PINB, &PORTB, 7}, // B7 13
{&DDRJ, &PINJ, &PORTJ, 1}, // J1 14
{&DDRJ, &PINJ, &PORTJ, 0}, // J0 15
{&DDRH, &PINH, &PORTH, 1}, // H1 16
{&DDRH, &PINH, &PORTH, 0}, // H0 17
{&DDRD, &PIND, &PORTD, 3}, // D3 18
{&DDRD, &PIND, &PORTD, 2}, // D2 19
{&DDRD, &PIND, &PORTD, 1}, // D1 20
{&DDRD, &PIND, &PORTD, 0}, // D0 21
{&DDRA, &PINA, &PORTA, 0}, // A0 22
{&DDRA, &PINA, &PORTA, 1}, // A1 23
{&DDRA, &PINA, &PORTA, 2}, // A2 24
{&DDRA, &PINA, &PORTA, 3}, // A3 25
{&DDRA, &PINA, &PORTA, 4}, // A4 26
{&DDRA, &PINA, &PORTA, 5}, // A5 27
{&DDRA, &PINA, &PORTA, 6}, // A6 28
{&DDRA, &PINA, &PORTA, 7}, // A7 29
{&DDRC, &PINC, &PORTC, 7}, // C7 30
{&DDRC, &PINC, &PORTC, 6}, // C6 31
{&DDRC, &PINC, &PORTC, 5}, // C5 32
{&DDRC, &PINC, &PORTC, 4}, // C4 33
{&DDRC, &PINC, &PORTC, 3}, // C3 34
{&DDRC, &PINC, &PORTC, 2}, // C2 35
{&DDRC, &PINC, &PORTC, 1}, // C1 36
{&DDRC, &PINC, &PORTC, 0}, // C0 37
{&DDRD, &PIND, &PORTD, 7}, // D7 38
{&DDRG, &PING, &PORTG, 2}, // G2 39
{&DDRG, &PING, &PORTG, 1}, // G1 40
{&DDRG, &PING, &PORTG, 0}, // G0 41
{&DDRL, &PINL, &PORTL, 7}, // L7 42
{&DDRL, &PINL, &PORTL, 6}, // L6 43
{&DDRL, &PINL, &PORTL, 5}, // L5 44
{&DDRL, &PINL, &PORTL, 4}, // L4 45
{&DDRL, &PINL, &PORTL, 3}, // L3 46
{&DDRL, &PINL, &PORTL, 2}, // L2 47
{&DDRL, &PINL, &PORTL, 1}, // L1 48
{&DDRL, &PINL, &PORTL, 0}, // L0 49
{&DDRB, &PINB, &PORTB, 3}, // B3 50
{&DDRB, &PINB, &PORTB, 2}, // B2 51
{&DDRB, &PINB, &PORTB, 1}, // B1 52
{&DDRB, &PINB, &PORTB, 0}, // B0 53
{&DDRF, &PINF, &PORTF, 0}, // F0 54
{&DDRF, &PINF, &PORTF, 1}, // F1 55
{&DDRF, &PINF, &PORTF, 2}, // F2 56
{&DDRF, &PINF, &PORTF, 3}, // F3 57
{&DDRF, &PINF, &PORTF, 4}, // F4 58
{&DDRF, &PINF, &PORTF, 5}, // F5 59
{&DDRF, &PINF, &PORTF, 6}, // F6 60
{&DDRF, &PINF, &PORTF, 7}, // F7 61
{&DDRK, &PINK, &PORTK, 0}, // K0 62
{&DDRK, &PINK, &PORTK, 1}, // K1 63
{&DDRK, &PINK, &PORTK, 2}, // K2 64
{&DDRK, &PINK, &PORTK, 3}, // K3 65
{&DDRK, &PINK, &PORTK, 4}, // K4 66
{&DDRK, &PINK, &PORTK, 5}, // K5 67
{&DDRK, &PINK, &PORTK, 6}, // K6 68
{&DDRK, &PINK, &PORTK, 7} // K7 69
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega644P__)\
|| defined(__AVR_ATmega644__)\
|| defined(__AVR_ATmega1284P__)
// Sanguino
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 17; // C1
uint8_t const SCL_PIN = 18; // C2
// SPI port
uint8_t const SS_PIN = 4; // B4
uint8_t const MOSI_PIN = 5; // B5
uint8_t const MISO_PIN = 6; // B6
uint8_t const SCK_PIN = 7; // B7
static const pin_map_t digitalPinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 4}, // B4 4
{&DDRB, &PINB, &PORTB, 5}, // B5 5
{&DDRB, &PINB, &PORTB, 6}, // B6 6
{&DDRB, &PINB, &PORTB, 7}, // B7 7
{&DDRD, &PIND, &PORTD, 0}, // D0 8
{&DDRD, &PIND, &PORTD, 1}, // D1 9
{&DDRD, &PIND, &PORTD, 2}, // D2 10
{&DDRD, &PIND, &PORTD, 3}, // D3 11
{&DDRD, &PIND, &PORTD, 4}, // D4 12
{&DDRD, &PIND, &PORTD, 5}, // D5 13
{&DDRD, &PIND, &PORTD, 6}, // D6 14
{&DDRD, &PIND, &PORTD, 7}, // D7 15
{&DDRC, &PINC, &PORTC, 0}, // C0 16
{&DDRC, &PINC, &PORTC, 1}, // C1 17
{&DDRC, &PINC, &PORTC, 2}, // C2 18
{&DDRC, &PINC, &PORTC, 3}, // C3 19
{&DDRC, &PINC, &PORTC, 4}, // C4 20
{&DDRC, &PINC, &PORTC, 5}, // C5 21
{&DDRC, &PINC, &PORTC, 6}, // C6 22
{&DDRC, &PINC, &PORTC, 7}, // C7 23
{&DDRA, &PINA, &PORTA, 7}, // A7 24
{&DDRA, &PINA, &PORTA, 6}, // A6 25
{&DDRA, &PINA, &PORTA, 5}, // A5 26
{&DDRA, &PINA, &PORTA, 4}, // A4 27
{&DDRA, &PINA, &PORTA, 3}, // A3 28
{&DDRA, &PINA, &PORTA, 2}, // A2 29
{&DDRA, &PINA, &PORTA, 1}, // A1 30
{&DDRA, &PINA, &PORTA, 0} // A0 31
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega32U4__)
// Teensy 2.0
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 6; // D1
uint8_t const SCL_PIN = 5; // D0
// SPI port
uint8_t const SS_PIN = 0; // B0
uint8_t const MOSI_PIN = 2; // B2
uint8_t const MISO_PIN = 3; // B3
uint8_t const SCK_PIN = 1; // B1
static const pin_map_t digitalPinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 7}, // B7 4
{&DDRD, &PIND, &PORTD, 0}, // D0 5
{&DDRD, &PIND, &PORTD, 1}, // D1 6
{&DDRD, &PIND, &PORTD, 2}, // D2 7
{&DDRD, &PIND, &PORTD, 3}, // D3 8
{&DDRC, &PINC, &PORTC, 6}, // C6 9
{&DDRC, &PINC, &PORTC, 7}, // C7 10
{&DDRD, &PIND, &PORTD, 6}, // D6 11
{&DDRD, &PIND, &PORTD, 7}, // D7 12
{&DDRB, &PINB, &PORTB, 4}, // B4 13
{&DDRB, &PINB, &PORTB, 5}, // B5 14
{&DDRB, &PINB, &PORTB, 6}, // B6 15
{&DDRF, &PINF, &PORTF, 7}, // F7 16
{&DDRF, &PINF, &PORTF, 6}, // F6 17
{&DDRF, &PINF, &PORTF, 5}, // F5 18
{&DDRF, &PINF, &PORTF, 4}, // F4 19
{&DDRF, &PINF, &PORTF, 1}, // F1 20
{&DDRF, &PINF, &PORTF, 0}, // F0 21
{&DDRD, &PIND, &PORTD, 4}, // D4 22
{&DDRD, &PIND, &PORTD, 5}, // D5 23
{&DDRE, &PINE, &PORTE, 6} // E6 24
};
//------------------------------------------------------------------------------
#elif defined(__AVR_AT90USB646__)\
|| defined(__AVR_AT90USB1286__)
// Teensy++ 1.0 & 2.0
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 1; // D1
uint8_t const SCL_PIN = 0; // D0
// SPI port
uint8_t const SS_PIN = 20; // B0
uint8_t const MOSI_PIN = 22; // B2
uint8_t const MISO_PIN = 23; // B3
uint8_t const SCK_PIN = 21; // B1
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRE, &PINE, &PORTE, 0}, // E0 8
{&DDRE, &PINE, &PORTE, 1}, // E1 9
{&DDRC, &PINC, &PORTC, 0}, // C0 10
{&DDRC, &PINC, &PORTC, 1}, // C1 11
{&DDRC, &PINC, &PORTC, 2}, // C2 12
{&DDRC, &PINC, &PORTC, 3}, // C3 13
{&DDRC, &PINC, &PORTC, 4}, // C4 14
{&DDRC, &PINC, &PORTC, 5}, // C5 15
{&DDRC, &PINC, &PORTC, 6}, // C6 16
{&DDRC, &PINC, &PORTC, 7}, // C7 17
{&DDRE, &PINE, &PORTE, 6}, // E6 18
{&DDRE, &PINE, &PORTE, 7}, // E7 19
{&DDRB, &PINB, &PORTB, 0}, // B0 20
{&DDRB, &PINB, &PORTB, 1}, // B1 21
{&DDRB, &PINB, &PORTB, 2}, // B2 22
{&DDRB, &PINB, &PORTB, 3}, // B3 23
{&DDRB, &PINB, &PORTB, 4}, // B4 24
{&DDRB, &PINB, &PORTB, 5}, // B5 25
{&DDRB, &PINB, &PORTB, 6}, // B6 26
{&DDRB, &PINB, &PORTB, 7}, // B7 27
{&DDRA, &PINA, &PORTA, 0}, // A0 28
{&DDRA, &PINA, &PORTA, 1}, // A1 29
{&DDRA, &PINA, &PORTA, 2}, // A2 30
{&DDRA, &PINA, &PORTA, 3}, // A3 31
{&DDRA, &PINA, &PORTA, 4}, // A4 32
{&DDRA, &PINA, &PORTA, 5}, // A5 33
{&DDRA, &PINA, &PORTA, 6}, // A6 34
{&DDRA, &PINA, &PORTA, 7}, // A7 35
{&DDRE, &PINE, &PORTE, 4}, // E4 36
{&DDRE, &PINE, &PORTE, 5}, // E5 37
{&DDRF, &PINF, &PORTF, 0}, // F0 38
{&DDRF, &PINF, &PORTF, 1}, // F1 39
{&DDRF, &PINF, &PORTF, 2}, // F2 40
{&DDRF, &PINF, &PORTF, 3}, // F3 41
{&DDRF, &PINF, &PORTF, 4}, // F4 42
{&DDRF, &PINF, &PORTF, 5}, // F5 43
{&DDRF, &PINF, &PORTF, 6}, // F6 44
{&DDRF, &PINF, &PORTF, 7} // F7 45
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega168__)\
||defined(__AVR_ATmega168P__)\
||defined(__AVR_ATmega328P__)
// 168 and 328 Arduinos
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 18; // C4
uint8_t const SCL_PIN = 19; // C5
// SPI port
uint8_t const SS_PIN = 10; // B2
uint8_t const MOSI_PIN = 11; // B3
uint8_t const MISO_PIN = 12; // B4
uint8_t const SCK_PIN = 13; // B5
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRB, &PINB, &PORTB, 0}, // B0 8
{&DDRB, &PINB, &PORTB, 1}, // B1 9
{&DDRB, &PINB, &PORTB, 2}, // B2 10
{&DDRB, &PINB, &PORTB, 3}, // B3 11
{&DDRB, &PINB, &PORTB, 4}, // B4 12
{&DDRB, &PINB, &PORTB, 5}, // B5 13
{&DDRC, &PINC, &PORTC, 0}, // C0 14
{&DDRC, &PINC, &PORTC, 1}, // C1 15
{&DDRC, &PINC, &PORTC, 2}, // C2 16
{&DDRC, &PINC, &PORTC, 3}, // C3 17
{&DDRC, &PINC, &PORTC, 4}, // C4 18
{&DDRC, &PINC, &PORTC, 5} // C5 19
};
#else // defined(__AVR_ATmega1280__)
#error unknown chip
#endif // defined(__AVR_ATmega1280__)
//------------------------------------------------------------------------------
static const uint8_t digitalPinCount = sizeof(digitalPinMap)/sizeof(pin_map_t);
uint8_t badPinNumber(void)
__attribute__((error("Pin number is too large or not a constant")));
static inline __attribute__((always_inline))
bool getPinMode(uint8_t pin) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1;
} else {
return badPinNumber();
}
}
static inline __attribute__((always_inline))
void setPinMode(uint8_t pin, uint8_t mode) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
if (mode) {
*digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit;
} else {
*digitalPinMap[pin].ddr &= ~(1 << digitalPinMap[pin].bit);
}
} else {
badPinNumber();
}
}
static inline __attribute__((always_inline))
bool fastDigitalRead(uint8_t pin) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1;
} else {
return badPinNumber();
}
}
static inline __attribute__((always_inline))
void fastDigitalWrite(uint8_t pin, uint8_t value) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
if (value) {
*digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit;
} else {
*digitalPinMap[pin].port &= ~(1 << digitalPinMap[pin].bit);
}
} else {
badPinNumber();
}
}
#endif // Sd2PinMap_h
#endif

1828
Firmware/SdBaseFile.cpp Normal file

File diff suppressed because it is too large Load Diff

483
Firmware/SdBaseFile.h Normal file
View File

@ -0,0 +1,483 @@
/* 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
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef SdBaseFile_h
#define SdBaseFile_h
/**
* \file
* \brief SdBaseFile class
*/
#include "Marlin.h"
#include "SdFatConfig.h"
#include "SdVolume.h"
//------------------------------------------------------------------------------
/**
* \struct filepos_t
* \brief internal type for istream
* do not use in user apps
*/
struct filepos_t {
/** stream position */
uint32_t position;
/** cluster for position */
uint32_t cluster;
filepos_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;
//----------------------------------------------------------------------------
// helpers for stream classes
/** get position for streams
* \param[out] pos struct to receive position
*/
void getpos(filepos_t* pos);
/** set position for streams
* \param[out] pos struct with value for new position
*/
void setpos(filepos_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( uint8_t flags = 0, uint8_t indent = 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();
static void printFatDate(uint16_t fatDate);
static void printFatTime( uint16_t fatTime);
bool printName();
int16_t read();
int16_t read(void* buf, uint16_t nbyte);
int8_t readDir(dir_t* dir, char* longFilename);
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);
//------------------------------------------------------------------------------
private:
// 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( 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);
//------------------------------------------------------------------------------
// 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, char* longFilename) {return readDir(&dir, longFilename);} // 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
};
#endif // SdBaseFile_h
#endif

123
Firmware/SdFatConfig.h Normal file
View File

@ -0,0 +1,123 @@
/* 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
* <http://www.gnu.org/licenses/>.
*/
/**
* \file
* \brief configuration definitions
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef SdFatConfig_h
#define SdFatConfig_h
#include <stdint.h>
//------------------------------------------------------------------------------
/**
* 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
//------------------------------------------------------------------------------
/**
* 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.
* Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
*
* 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
//------------------------------------------------------------------------------
/**
* 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
/** Software SPI chip select pin for the SD */
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;
//------------------------------------------------------------------------------
/**
* The __cxa_pure_virtual function is an error handler that is invoked when
* a pure virtual function is called.
*/
#define USE_CXA_PURE_VIRTUAL 1
/**
* Defines for long (vfat) filenames
*/
/** Number of VFAT entries used. Every entry has 13 UTF-16 characters */
#define MAX_VFAT_ENTRIES (4)
/** Number of UTF-16 characters per entry */
#define FILENAME_LENGTH 13
/** Total size of the buffer used to store the long filenames */
#define LONG_FILENAME_LENGTH (13*MAX_VFAT_ENTRIES+1)
#endif // SdFatConfig_h
#endif

646
Firmware/SdFatStructs.h Normal file
View File

@ -0,0 +1,646 @@
/* 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
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef SdFatStructs_h
#define SdFatStructs_h
#define PACKED __attribute__((__packed__))
/**
* \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;
} PACKED;
/** 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;
} PACKED;
/** 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;
} PACKED;
/** 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;
} PACKED;
/** 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];
} PACKED;
/** 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;
} PACKED;
/**
* \struct directoryVFATEntry
* \brief VFAT long filename directory entry
*
* directoryVFATEntries are found in the same list as normal directoryEntry.
* But have the attribute field set to DIR_ATT_LONG_NAME.
*
* Long filenames are saved in multiple directoryVFATEntries.
* Each entry containing 13 UTF-16 characters.
*/
struct directoryVFATEntry {
/**
* Sequence number. Consists of 2 parts:
* bit 6: indicates first long filename block for the next file
* bit 0-4: the position of this long filename block (first block is 1)
*/
uint8_t sequenceNumber;
/** First set of UTF-16 characters */
uint16_t name1[5];//UTF-16
/** attributes (at the same location as in directoryEntry), always 0x0F */
uint8_t attributes;
/** Reserved for use by Windows NT. Always 0. */
uint8_t reservedNT;
/** Checksum of the short 8.3 filename, can be used to checked if the file system as modified by a not-long-filename aware implementation. */
uint8_t checksum;
/** Second set of UTF-16 characters */
uint16_t name2[6];//UTF-16
/** firstClusterLow is always zero for longFilenames */
uint16_t firstClusterLow;
/** Third set of UTF-16 characters */
uint16_t name3[2];//UTF-16
} PACKED;
//------------------------------------------------------------------------------
// Definitions for directory entries
//
/** Type name for directoryEntry */
typedef struct directoryEntry dir_t;
/** Type name for directoryVFATEntry */
typedef struct directoryVFATEntry vfat_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;
}
#endif // SdFatStructs_h
#endif

104
Firmware/SdFatUtil.cpp Normal file
View File

@ -0,0 +1,104 @@
/* Arduino SdFat Library
* Copyright (C) 2008 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
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#include "SdFatUtil.h"
//------------------------------------------------------------------------------
/** Amount of free RAM
* \return The number of free bytes.
*/
#ifdef __arm__
extern "C" char* sbrk(int incr);
int SdFatUtil::FreeRam() {
char top;
return &top - reinterpret_cast<char*>(sbrk(0));
}
#else // __arm__
extern char *__brkval;
extern char __bss_end;
/** Amount of free RAM
* \return The number of free bytes.
*/
int SdFatUtil::FreeRam() {
char top;
return __brkval ? &top - __brkval : &top - &__bss_end;
}
#endif // __arm
void SdFatUtil::set_stack_guard()
{
uint32_t *stack_guard;
stack_guard = (uint32_t*)&__bss_end;
*stack_guard = STACK_GUARD_TEST_VALUE;
}
bool SdFatUtil::test_stack_integrity()
{
uint32_t* stack_guard = (uint32_t*)&__bss_end;
return (*stack_guard == STACK_GUARD_TEST_VALUE);
}
uint32_t SdFatUtil::get_stack_guard_test_value()
{
uint32_t* stack_guard;
uint32_t output;
stack_guard = (uint32_t*)&__bss_end;
output = *stack_guard;
return(output);
}
//------------------------------------------------------------------------------
/** %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( PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) MYSERIAL.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( PGM_P str) {
print_P( str);
MYSERIAL.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(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( str);
}
#endif

51
Firmware/SdFatUtil.h Normal file
View File

@ -0,0 +1,51 @@
/* Arduino SdFat Library
* Copyright (C) 2008 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
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef SdFatUtil_h
#define SdFatUtil_h
/**
* \file
* \brief Useful utility functions.
*/
#include "Marlin.h"
#include "MarlinSerial.h"
/** 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( PGM_P str);
void println_P( PGM_P str);
void SerialPrint_P(PGM_P str);
void SerialPrintln_P(PGM_P str);
void set_stack_guard();
bool test_stack_integrity();
uint32_t get_stack_guard_test_value();
}
using namespace SdFatUtil; // NOLINT
#endif // #define SdFatUtil_h
#endif

95
Firmware/SdFile.cpp Normal file
View File

@ -0,0 +1,95 @@
/* 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
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#include "SdFile.h"
/** 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 writeError to check for errors.
*/
#if ARDUINO >= 100
size_t SdFile::write(uint8_t b)
{
return SdBaseFile::write(&b, 1);
}
#else
void SdFile::write(uint8_t b)
{
SdBaseFile::write(&b, 1);
}
#endif
//------------------------------------------------------------------------------
/** Write a string to a file. Used by the Arduino Print class.
* \param[in] str Pointer to the string.
* Use writeError to check for errors.
*/
void SdFile::write(const char* str) {
SdBaseFile::write(str, strlen(str));
}
//------------------------------------------------------------------------------
/** Write a PROGMEM string to a file.
* \param[in] str Pointer to the PROGMEM string.
* Use writeError 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 writeError to check for errors.
*/
void SdFile::writeln_P(PGM_P str) {
write_P(str);
write_P(PSTR("\r\n"));
}
#endif

54
Firmware/SdFile.h Normal file
View File

@ -0,0 +1,54 @@
/* 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
* <http://www.gnu.org/licenses/>.
*/
/**
* \file
* \brief SdFile class
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#include "SdBaseFile.h"
//#include <Print.h>
#ifndef SdFile_h
#define SdFile_h
//------------------------------------------------------------------------------
/**
* \class SdFile
* \brief SdBaseFile with Print.
*/
class SdFile : public SdBaseFile/*, public Print*/ {
public:
SdFile() {}
SdFile(const char* name, uint8_t oflag);
#if ARDUINO >= 100
size_t write(uint8_t b);
#else
void write(uint8_t b);
#endif
int16_t write(const void* buf, uint16_t nbyte);
void write(const char* str);
void write_P(PGM_P str);
void writeln_P(PGM_P str);
};
#endif // SdFile_h
#endif

286
Firmware/SdInfo.h Normal file
View File

@ -0,0 +1,286 @@
/* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card 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 Sd2Card Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef SdInfo_h
#define SdInfo_h
#include <stdint.h>
// 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;
/** Toshiba FlashAir: iSDIO */
uint8_t const CMD48 = 0x30;
/** Toshiba FlashAir: iSDIO */
uint8_t const CMD49 = 0x31;
/** 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;
/** 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
/** not used */
unsigned char reserved3 : 2;
/** high part of card size */
unsigned char c_size_high : 6;
// 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;
};
#endif // SdInfo_h
#endif

405
Firmware/SdVolume.cpp Normal file
View File

@ -0,0 +1,405 @@
/* 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
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#include "SdVolume.h"
//------------------------------------------------------------------------------
#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;
}
#endif

214
Firmware/SdVolume.h Normal file
View File

@ -0,0 +1,214 @@
/* 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
* <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#ifdef SDSUPPORT
#ifndef SdVolume_h
#define SdVolume_h
/**
* \file
* \brief SdVolume class
*/
#include "SdFatConfig.h"
#include "Sd2Card.h"
#include "SdFatStructs.h"
//==============================================================================
// 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_);}
uint32_t blockNumber(uint32_t cluster, uint32_t position) const {
return clusterStartBlock(cluster) + blockOfCluster(position);}
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
};
#endif // SdVolume
#endif

344
Firmware/Servo.cpp Normal file
View File

@ -0,0 +1,344 @@
/*
Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
Copyright (c) 2009 Michael Margolis. 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
*/
/*
A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
The servos are pulsed in the background using the value most recently written using the write() method
Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
The methods are:
Servo - Class for manipulating servo motors connected to Arduino pins.
attach(pin ) - Attaches a servo motor to an i/o pin.
attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
default min is 544, max is 2400
write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
writeMicroseconds() - Sets the servo pulse width in microseconds
read() - Gets the last written servo pulse width as an angle between 0 and 180.
readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
attached() - Returns true if there is a servo attached.
detach() - Stops an attached servos from pulsing its i/o pin.
*/
#include "Configuration.h"
#ifdef NUM_SERVOS
#include <avr/interrupt.h>
#include <Arduino.h>
#include "Servo.h"
#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009
#define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds
#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009
//#define NBR_TIMERS (MAX_SERVOS / SERVOS_PER_TIMER)
static servo_t servos[MAX_SERVOS]; // static array of servo structures
static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval)
uint8_t ServoCount = 0; // the total number of attached servos
// convenience macros
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
/************ static functions common to all instances ***********************/
static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
{
if( Channel[timer] < 0 )
*TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
else{
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
}
Channel[timer]++; // increment to the next channel
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
*OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
}
else {
// finished all channels so wait for the refresh period to expire before starting over
if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed
*OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
else
*OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
}
}
#ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform
// Interrupt handlers for Arduino
#if defined(_useTimer1)
SIGNAL (TIMER1_COMPA_vect)
{
handle_interrupts(_timer1, &TCNT1, &OCR1A);
}
#endif
#if defined(_useTimer3)
SIGNAL (TIMER3_COMPA_vect)
{
handle_interrupts(_timer3, &TCNT3, &OCR3A);
}
#endif
#if defined(_useTimer4)
SIGNAL (TIMER4_COMPA_vect)
{
handle_interrupts(_timer4, &TCNT4, &OCR4A);
}
#endif
#if defined(_useTimer5)
SIGNAL (TIMER5_COMPA_vect)
{
handle_interrupts(_timer5, &TCNT5, &OCR5A);
}
#endif
#elif defined WIRING
// Interrupt handlers for Wiring
#if defined(_useTimer1)
void Timer1Service()
{
handle_interrupts(_timer1, &TCNT1, &OCR1A);
}
#endif
#if defined(_useTimer3)
void Timer3Service()
{
handle_interrupts(_timer3, &TCNT3, &OCR3A);
}
#endif
#endif
static void initISR(timer16_Sequence_t timer)
{
#if defined (_useTimer1)
if(timer == _timer1) {
TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // set prescaler of 8
TCNT1 = 0; // clear the timer count
#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
TIFR |= _BV(OCF1A); // clear any pending interrupts;
TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt
#else
// here if not ATmega8 or ATmega128
TIFR1 |= _BV(OCF1A); // clear any pending interrupts;
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
#endif
#if defined(WIRING)
timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
#endif
}
#endif
#if defined (_useTimer3)
if(timer == _timer3) {
TCCR3A = 0; // normal counting mode
TCCR3B = _BV(CS31); // set prescaler of 8
TCNT3 = 0; // clear the timer count
#if defined(__AVR_ATmega128__)
TIFR |= _BV(OCF3A); // clear any pending interrupts;
ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt
#else
TIFR3 = _BV(OCF3A); // clear any pending interrupts;
TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt
#endif
#if defined(WIRING)
timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only
#endif
}
#endif
#if defined (_useTimer4)
if(timer == _timer4) {
TCCR4A = 0; // normal counting mode
TCCR4B = _BV(CS41); // set prescaler of 8
TCNT4 = 0; // clear the timer count
TIFR4 = _BV(OCF4A); // clear any pending interrupts;
TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt
}
#endif
#if defined (_useTimer5)
if(timer == _timer5) {
TCCR5A = 0; // normal counting mode
TCCR5B = _BV(CS51); // set prescaler of 8
TCNT5 = 0; // clear the timer count
TIFR5 = _BV(OCF5A); // clear any pending interrupts;
TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt
}
#endif
}
static void finISR(timer16_Sequence_t timer)
{
//disable use of the given timer
#if defined WIRING // Wiring
if(timer == _timer1) {
#if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
TIMSK1 &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
#else
TIMSK &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
#endif
timerDetach(TIMER1OUTCOMPAREA_INT);
}
else if(timer == _timer3) {
#if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
TIMSK3 &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt
#else
ETIMSK &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt
#endif
timerDetach(TIMER3OUTCOMPAREA_INT);
}
#else
//For arduino - in future: call here to a currently undefined function to reset the timer
#endif
}
static boolean isTimerActive(timer16_Sequence_t timer)
{
// returns true if any servo is active on this timer
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
if(SERVO(timer,channel).Pin.isActive == true)
return true;
}
return false;
}
/****************** end of static functions ******************************/
Servo::Servo()
{
if( ServoCount < MAX_SERVOS) {
this->servoIndex = ServoCount++; // assign a servo index to this instance
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009
}
else
this->servoIndex = INVALID_SERVO ; // too many servos
}
uint8_t Servo::attach(int pin)
{
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
}
uint8_t Servo::attach(int pin, int min, int max)
{
if(this->servoIndex < MAX_SERVOS ) {
#if defined (ENABLE_AUTO_BED_LEVELING) && (PROBE_SERVO_DEACTIVATION_DELAY > 0)
if (pin > 0) this->pin = pin; else pin = this->pin;
#endif
pinMode( pin, OUTPUT) ; // set servo pin to output
servos[this->servoIndex].Pin.nbr = pin;
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
this->max = (MAX_PULSE_WIDTH - max)/4;
// initialize the timer if it has not already been initialized
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
if(isTimerActive(timer) == false)
initISR(timer);
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
}
return this->servoIndex ;
}
void Servo::detach()
{
servos[this->servoIndex].Pin.isActive = false;
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
if(isTimerActive(timer) == false) {
finISR(timer);
}
}
void Servo::write(int value)
{
if(value < MIN_PULSE_WIDTH)
{ // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
if(value < 0) value = 0;
if(value > 180) value = 180;
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
}
this->writeMicroseconds(value);
}
void Servo::writeMicroseconds(int value)
{
// calculate and store the values for the given channel
byte channel = this->servoIndex;
if( (channel < MAX_SERVOS) ) // ensure channel is valid
{
if( value < SERVO_MIN() ) // ensure pulse width is valid
value = SERVO_MIN();
else if( value > SERVO_MAX() )
value = SERVO_MAX();
value = value - TRIM_DURATION;
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009
uint8_t oldSREG = SREG;
cli();
servos[channel].ticks = value;
SREG = oldSREG;
}
}
int Servo::read() // return the value as degrees
{
return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
}
int Servo::readMicroseconds()
{
unsigned int pulsewidth;
if( this->servoIndex != INVALID_SERVO )
pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION ; // 12 aug 2009
else
pulsewidth = 0;
return pulsewidth;
}
bool Servo::attached()
{
return servos[this->servoIndex].Pin.isActive ;
}
#endif

135
Firmware/Servo.h Normal file
View File

@ -0,0 +1,135 @@
/*
Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
Copyright (c) 2009 Michael Margolis. 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
*/
/*
A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
The servos are pulsed in the background using the value most recently written using the write() method
Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
The sequence used to seize timers is defined in timers.h
The methods are:
Servo - Class for manipulating servo motors connected to Arduino pins.
attach(pin ) - Attaches a servo motor to an i/o pin.
attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
default min is 544, max is 2400
write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
writeMicroseconds() - Sets the servo pulse width in microseconds
read() - Gets the last written servo pulse width as an angle between 0 and 180.
readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
attached() - Returns true if there is a servo attached.
detach() - Stops an attached servos from pulsing its i/o pin.
*/
#ifndef Servo_h
#define Servo_h
#include <inttypes.h>
/*
* Defines for 16 bit timers used with Servo library
*
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
* _Nbr_16timers indicates how many 16 bit timers are available.
*
*/
// Say which 16 bit timers can be used and in what order
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define _useTimer5
//#define _useTimer1
#define _useTimer3
#define _useTimer4
//typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ;
typedef enum { _timer5, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ;
#elif defined(__AVR_ATmega32U4__)
//#define _useTimer1
#define _useTimer3
//typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t ;
typedef enum { _timer3, _Nbr_16timers } timer16_Sequence_t ;
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define _useTimer3
//#define _useTimer1
//typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;
typedef enum { _timer3, _Nbr_16timers } timer16_Sequence_t ;
#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__) || defined(__AVR_ATmega1284P__) ||defined(__AVR_ATmega2561__)
#define _useTimer3
//#define _useTimer1
//typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;
typedef enum { _timer3, _Nbr_16timers } timer16_Sequence_t ;
#else // everything else
//#define _useTimer1
//typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t ;
typedef enum { _Nbr_16timers } timer16_Sequence_t ;
#endif
#define Servo_VERSION 2 // software version of this library
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds
#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
#define INVALID_SERVO 255 // flag indicating an invalid servo index
typedef struct {
uint8_t nbr :6 ; // a pin number from 0 to 63
uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
} ServoPin_t ;
typedef struct {
ServoPin_t Pin;
unsigned int ticks;
} servo_t;
class Servo
{
public:
Servo();
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
void detach();
void write(int value); // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // Write pulse width in microseconds
int read(); // returns current pulse width as an angle between 0 and 180 degrees
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
bool attached(); // return true if this servo is attached, otherwise false
#if defined (ENABLE_AUTO_BED_LEVELING) && (PROBE_SERVO_DEACTIVATION_DELAY > 0)
int pin; // store the hardware pin of the servo
#endif
private:
uint8_t servoIndex; // index into the channel data for this servo
int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
};
#endif

68
Firmware/Timer.cpp Normal file
View File

@ -0,0 +1,68 @@
/**
* @file
* @author Marek Bel
*/
#include "Timer.h"
#include "system_timer.h"
/**
* @brief construct Timer
*
* It is guaranteed, that construction is equivalent with zeroing all members.
* This property can be exploited in menu_data.
*/
template<typename T>
Timer<T>::Timer() : m_isRunning(false), m_started()
{
}
/**
* @brief Start timer
*/
template<typename T>
void Timer<T>::start()
{
m_started = _millis();
m_isRunning = true;
}
/**
* @brief Timer has expired
*
* Timer is considered expired after msPeriod has passed from time the timer was started.
* Timer is stopped after expiration.
* This function must be called at least each (T maximum value - msPeriod) milliseconds to be sure to
* catch first expiration.
* This function is expected to handle wrap around of time register well.
*
* @param msPeriod Time interval in milliseconds. Do not omit "ul" when using constant literal with LongTimer.
* @retval true Timer has expired
* @retval false Timer not expired yet, or is not running, or time window in which is timer considered expired passed.
*/
template<typename T>
bool Timer<T>::expired(T msPeriod)
{
if (!m_isRunning) return false;
bool expired = false;
const T now = _millis();
if (m_started <= m_started + msPeriod)
{
if ((now >= m_started + msPeriod) || (now < m_started))
{
expired = true;
}
}
else
{
if ((now >= m_started + msPeriod) && (now < m_started))
{
expired = true;
}
}
if (expired) m_isRunning = false;
return expired;
}
template class Timer<unsigned long>;
template class Timer<unsigned short>;

53
Firmware/Timer.h Normal file
View File

@ -0,0 +1,53 @@
/**
* @file
* @author Marek Bel
*/
#ifndef TIMER_H
#define TIMER_H
/**
* @brief simple timer
*
* Simple and memory saving implementation. Should handle timer register wrap around well.
* Resolution is one millisecond. To save memory, doesn't store timer period.
* If you wish timer which is storing period, derive from this.
*/
template <class T>
class Timer
{
public:
Timer();
void start();
void stop(){m_isRunning = false;}
bool running(){return m_isRunning;}
bool expired(T msPeriod);
protected:
T started(){return m_started;}
private:
bool m_isRunning;
T m_started;
};
/**
* @brief Timer unsigned long specialization
*
* Maximum period is at least 49 days.
*/
#if __cplusplus>=201103L
using LongTimer = Timer<unsigned long>;
#else
typedef Timer<unsigned long> LongTimer;
#endif
/**
* @brief Timer unsigned short specialization
*
* Maximum period is at least 65 seconds.
*/
#if __cplusplus>=201103L
using ShortTimer = Timer<unsigned short>;
#else
typedef Timer<unsigned short> ShortTimer;
#endif
#endif /* TIMER_H */

56
Firmware/TimerRemaining.h Normal file
View File

@ -0,0 +1,56 @@
/**
* @file
* @author Marek Bel
*/
#ifndef TIMERREMAINING_H
#define TIMERREMAINING_H
#include "Timer.h"
#include "Arduino.h"
#include "system_timer.h"
#include <limits.h>
class TimerRemaining : public LongTimer
{
public:
TimerRemaining() : m_period(){}
void start() = delete;
bool expired(unsigned long msPeriod) = delete;
/**
* @brief Start timer
* @param msPeriod Time to expire in milliseconds
*/
void start(unsigned long msPeriod)
{
m_period = msPeriod;
LongTimer::start();
}
/**
* @brief Time remaining to expiration
*
* @param msPeriod timer period in milliseconds
* @return time remaining to expiration in milliseconds
* @retval 0 Timer has expired, or was not even started.
*/
unsigned long remaining()
{
if (!running()) return 0;
if (expired()) return 0;
const unsigned long now = _millis();
return (started() + m_period - now);
}
/**
* @brief Timer has expired.
* @retval true Timer has expired.
* @retval false Timer has not expired.
*/
bool expired()
{
return LongTimer::expired(m_period);
}
private:
unsigned long m_period; //!< Timer period in milliseconds.
};
#endif // ifndef TIMERREMAINING_H

94
Firmware/adc.c Normal file
View File

@ -0,0 +1,94 @@
//adc.c
#include "adc.h"
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
uint8_t adc_state;
uint8_t adc_count;
uint16_t adc_values[ADC_CHAN_CNT];
uint16_t adc_sim_mask;
#ifdef ADC_CALLBACK
extern void ADC_CALLBACK(void);
#endif //ADC_CALLBACK
void adc_init(void)
{
printf_P(PSTR("adc_init\n"));
adc_sim_mask = 0x00;
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
ADMUX |= (1 << REFS0);
ADCSRA |= (1 << ADEN);
// ADCSRA |= (1 << ADIF) | (1 << ADSC);
DIDR0 = (ADC_CHAN_MSK & 0xff);
DIDR2 = (ADC_CHAN_MSK >> 8);
adc_reset();
// adc_sim_mask = 0b0101;
// adc_sim_mask = 0b100101;
// adc_values[0] = 1023 * 16;
// adc_values[2] = 1023 * 16;
// adc_values[5] = 1002 * 16;
}
void adc_reset(void)
{
adc_state = 0;
adc_count = 0;
uint8_t i; for (i = 0; i < ADC_CHAN_CNT; i++)
if ((adc_sim_mask & (1 << i)) == 0)
adc_values[i] = 0;
}
void adc_setmux(uint8_t ch)
{
ch &= 0x0f;
if (ch & 0x08) ADCSRB |= (1 << MUX5);
else ADCSRB &= ~(1 << MUX5);
ADMUX = (ADMUX & ~(0x07)) | (ch & 0x07);
}
uint8_t adc_chan(uint8_t index)
{
uint8_t chan = 0;
uint16_t mask = 1;
while (mask)
{
if ((mask & ADC_CHAN_MSK) && (index-- == 0)) break;
mask <<= 1;
chan++;
}
return chan;
}
void adc_cycle(void)
{
if (adc_state & 0x80)
{
uint8_t index = adc_state & 0x0f;
if ((adc_sim_mask & (1 << index)) == 0)
adc_values[index] += ADC;
if (++index >= ADC_CHAN_CNT)
{
index = 0;
adc_count++;
if (adc_count >= ADC_OVRSAMPL)
{
#ifdef ADC_CALLBACK
ADC_CALLBACK();
#endif //ADC_CALLBACK
adc_reset();
}
}
adc_setmux(adc_chan(index));
adc_state = index;
}
else
{
ADCSRA |= (1 << ADSC); //start conversion
adc_state |= 0x80;
}
}

45
Firmware/adc.h Normal file
View File

@ -0,0 +1,45 @@
//adc.h
#ifndef _ADC_H
#define _ADC_H
#include <inttypes.h>
#include "config.h"
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
/*
http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html
*/
#define BITCOUNT(x) (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
#define BX_(x) ((x) - (((x)>>1)&0x77777777) - (((x)>>2)&0x33333333) - (((x)>>3)&0x11111111))
#define ADC_PIN_IDX(pin) BITCOUNT(ADC_CHAN_MSK & ((1 << (pin)) - 1))
#if BITCOUNT(ADC_CHAN_MSK) != ADC_CHAN_CNT
# error "ADC_CHAN_MSK oes not match ADC_CHAN_CNT"
#endif
extern uint8_t adc_state;
extern uint8_t adc_count;
extern uint16_t adc_values[ADC_CHAN_CNT];
extern uint16_t adc_sim_mask;
extern void adc_init(void);
extern void adc_reset(void);
extern void adc_setmux(uint8_t ch);
extern uint8_t adc_chan(uint8_t index);
extern void adc_cycle(void);
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)
#endif //_ADC_H

14
Firmware/boards.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef BOARDS_H
#define BOARDS_H
#define BOARD_UNKNOWN -1
#define BOARD_RAMBO_MINI_1_0 200 // Rambo-mini 1.0 - 200 (orig 102)
#define BOARD_RAMBO_MINI_1_3 203 // Rambo-mini 1.3 - 203 (orig 302)
#define BOARD_EINSY_1_0a 310 // EINSy 1.0a - 310 (new)
#define MB(board) (MOTHERBOARD==BOARD_##board)
#define IS_RAMPS (MB(RAMPS_OLD) || MB(RAMPS_13_EFB) || MB(RAMPS_13_EEB) || MB(RAMPS_13_EFF) || MB(RAMPS_13_EEF))
#endif //__BOARDS_H

55
Firmware/bootapp.c Normal file
View File

@ -0,0 +1,55 @@
//bootapp.c
#include "bootapp.h"
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <stdio.h>
extern FILE _uartout;
#define uartout (&_uartout)
void bootapp_print_vars(void)
{
fprintf_P(uartout, PSTR("boot_src_addr =0x%08lx\n"), boot_src_addr);
fprintf_P(uartout, PSTR("boot_dst_addr =0x%08lx\n"), boot_dst_addr);
fprintf_P(uartout, PSTR("boot_copy_size =0x%04x\n"), boot_copy_size);
fprintf_P(uartout, PSTR("boot_reserved =0x%02x\n"), boot_reserved);
fprintf_P(uartout, PSTR("boot_app_flags =0x%02x\n"), boot_app_flags);
fprintf_P(uartout, PSTR("boot_app_magic =0x%08lx\n"), boot_app_magic);
}
void bootapp_ram2flash(uint16_t rptr, uint16_t fptr, uint16_t size)
{
cli();
boot_app_magic = BOOT_APP_MAGIC;
boot_app_flags |= BOOT_APP_FLG_COPY;
boot_app_flags |= BOOT_APP_FLG_ERASE;
/* uint16_t ui; for (ui = 0; ui < size; ui++)
{
uint8_t uc = ram_array[ui+rptr];
if (pgm_read_byte(ui+fptr) & uc != uc)
{
boot_app_flags |= BOOT_APP_FLG_ERASE;
break;
}
}*/
boot_copy_size = (uint16_t)size;
boot_src_addr = (uint32_t)rptr;
boot_dst_addr = (uint32_t)fptr;
bootapp_print_vars();
wdt_enable(WDTO_15MS);
while(1);
}
void bootapp_reboot_user0(uint8_t reserved)
{
cli();
boot_app_magic = BOOT_APP_MAGIC;
boot_app_flags = BOOT_APP_FLG_USER0;
boot_reserved = reserved;
bootapp_print_vars();
wdt_enable(WDTO_15MS);
while(1);
}

41
Firmware/bootapp.h Normal file
View File

@ -0,0 +1,41 @@
//language.h
#ifndef BOOTAPP_H
#define BOOTAPP_H
#include "config.h"
#include <inttypes.h>
#define RAMSIZE 0x2000
#define ram_array ((uint8_t*)(0))
#define boot_src_addr (*((uint32_t*)(RAMSIZE - 16)))
#define boot_dst_addr (*((uint32_t*)(RAMSIZE - 12)))
#define boot_copy_size (*((uint16_t*)(RAMSIZE - 8)))
#define boot_reserved (*((uint8_t*)(RAMSIZE - 6)))
#define boot_app_flags (*((uint8_t*)(RAMSIZE - 5)))
#define boot_app_magic (*((uint32_t*)(RAMSIZE - 4)))
#define BOOT_APP_FLG_ERASE 0x01
#define BOOT_APP_FLG_COPY 0x02
#define BOOT_APP_FLG_FLASH 0x04
#define BOOT_APP_FLG_RUN 0x08
#define BOOT_APP_FLG_USER0 0x80
#define BOOT_APP_MAGIC 0x55aa55aa
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
extern void bootapp_print_vars(void);
extern void bootapp_ram2flash(uint16_t rptr, uint16_t fptr, uint16_t size);
extern void bootapp_reboot_user0(uint8_t reserved);
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)
#endif //BOOTAPP_H

1023
Firmware/cardreader.cpp Normal file

File diff suppressed because it is too large Load Diff

184
Firmware/cardreader.h Normal file
View File

@ -0,0 +1,184 @@
#ifndef CARDREADER_H
#define CARDREADER_H
#ifdef SDSUPPORT
#define MAX_DIR_DEPTH 10
#include "SdFile.h"
enum LsAction {LS_SerialPrint,LS_Count,LS_GetFilename};
class CardReader
{
public:
CardReader();
void initsd();
void write_command(char *buf);
void write_command_no_newline(char *buf);
//files auto[0-9].g on the sd card are performed in a row
//this is to delay autostart and hence the initialisaiton of the sd card to some seconds after the normal init, so the device is available quick after a reset
void checkautostart(bool x);
void openFile(const char* name,bool read,bool replace_current=true);
void openLogFile(const char* name);
void removeFile(const char* name);
void closefile(bool store_location=false);
void release();
void startFileprint();
void pauseSDPrint();
uint32_t getFileSize();
void getStatus();
void printingHasFinished();
void getfilename(uint16_t nr, const char* const match=NULL);
void getfilename_simple(uint32_t position, const char * const match = NULL);
uint16_t getnrfilenames();
void getAbsFilename(char *t);
void getDirName(char* name, uint8_t level);
uint16_t getWorkDirDepth();
void ls();
void chdir(const char * relpath);
void updir();
void setroot();
#ifdef SDCARD_SORT_ALPHA
void presort();
#ifdef SDSORT_QUICKSORT
void swap(uint8_t left, uint8_t right);
void quicksort(uint8_t left, uint8_t right);
#endif //SDSORT_QUICKSORT
void getfilename_sorted(const uint16_t nr);
#if SDSORT_GCODE
FORCE_INLINE void setSortOn(bool b) { sort_alpha = b; presort(); }
FORCE_INLINE void setSortFolders(int i) { sort_folders = i; presort(); }
//FORCE_INLINE void setSortReverse(bool b) { sort_reverse = b; }
#endif
#endif
FORCE_INLINE bool isFileOpen() { return file.isOpen(); }
FORCE_INLINE bool eof() { return sdpos>=filesize ;};
FORCE_INLINE int16_t get() { sdpos = file.curPosition();return (int16_t)file.read();};
FORCE_INLINE void setIndex(long index) {sdpos = index;file.seekSet(index);};
FORCE_INLINE uint8_t percentDone(){if(!isFileOpen()) return 0; if(filesize) return sdpos/((filesize+99)/100); else return 0;};
FORCE_INLINE char* getWorkDirName(){workDir.getFilename(filename);return filename;};
FORCE_INLINE uint32_t get_sdpos() { if (!isFileOpen()) return 0; else return(sdpos); };
bool ToshibaFlashAir_isEnabled() const { return card.getFlashAirCompatible(); }
void ToshibaFlashAir_enable(bool enable) { card.setFlashAirCompatible(enable); }
bool ToshibaFlashAir_GetIP(uint8_t *ip);
public:
bool saving;
bool logging;
bool sdprinting ;
bool cardOK ;
bool paused ;
char filename[13];
uint16_t creationTime, creationDate;
uint32_t cluster, position;
char longFilename[LONG_FILENAME_LENGTH];
bool filenameIsDir;
int lastnr; //last number of the autostart;
private:
SdFile root,*curDir,workDir,workDirParents[MAX_DIR_DEPTH];
uint16_t workDirDepth;
// Sort files and folders alphabetically.
#ifdef SDCARD_SORT_ALPHA
uint16_t sort_count; // Count of sorted items in the current directory
#if SDSORT_GCODE
bool sort_alpha; // Flag to enable / disable the feature
int sort_folders; // Flag to enable / disable folder sorting
//bool sort_reverse; // Flag to enable / disable reverse sorting
#endif
// By default the sort index is static
#if SDSORT_DYNAMIC_RAM
uint8_t *sort_order;
#else
uint8_t sort_order[SDSORT_LIMIT];
#endif
// Cache filenames to speed up SD menus.
#if SDSORT_USES_RAM
// If using dynamic ram for names, allocate on the heap.
#if SDSORT_CACHE_NAMES
#if SDSORT_DYNAMIC_RAM
char **sortshort, **sortnames;
#else
char sortshort[SDSORT_LIMIT][FILENAME_LENGTH];
char sortnames[SDSORT_LIMIT][FILENAME_LENGTH];
#endif
#elif !SDSORT_USES_STACK
char sortnames[SDSORT_LIMIT][FILENAME_LENGTH];
uint16_t creation_time[SDSORT_LIMIT];
uint16_t creation_date[SDSORT_LIMIT];
#endif
// Folder sorting uses an isDir array when caching items.
#if HAS_FOLDER_SORTING
#if SDSORT_DYNAMIC_RAM
uint8_t *isDir;
#elif (SDSORT_CACHE_NAMES) || !(SDSORT_USES_STACK)
uint8_t isDir[(SDSORT_LIMIT + 7) >> 3];
#endif
#endif
#endif // SDSORT_USES_RAM
#endif // SDCARD_SORT_ALPHA
#ifdef DEBUG_SD_SPEED_TEST
public:
#endif //DEBUG_SD_SPEED_TEST
Sd2Card card;
private:
SdVolume volume;
SdFile file;
#define SD_PROCEDURE_DEPTH 1
#define MAXPATHNAMELENGTH (13*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
uint8_t file_subcall_ctr;
uint32_t filespos[SD_PROCEDURE_DEPTH];
char filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];
uint32_t filesize;
//int16_t n;
unsigned long autostart_atmillis;
uint32_t sdpos ;
bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware.
LsAction lsAction; //stored for recursion.
int16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
char* diveDirName;
void diveSubfolder (const char *fileName, SdFile& dir);
void lsDive(const char *prepend, SdFile parent, const char * const match=NULL);
#ifdef SDCARD_SORT_ALPHA
void flush_presort();
#endif
};
extern bool Stopped;
extern CardReader card;
#define IS_SD_PRINTING (card.sdprinting)
#if (SDCARDDETECT > -1)
# ifdef SDCARDDETECTINVERTED
# define IS_SD_INSERTED (READ(SDCARDDETECT)!=0)
# else
# define IS_SD_INSERTED (READ(SDCARDDETECT)==0)
# endif //SDCARDTETECTINVERTED
#else
//If we don't have a card detect line, aways asume the card is inserted
# define IS_SD_INSERTED true
#endif
#else
#define IS_SD_PRINTING (false)
#endif //SDSUPPORT
#endif

701
Firmware/cmdqueue.cpp Normal file
View File

@ -0,0 +1,701 @@
#include "cmdqueue.h"
#include "cardreader.h"
#include "ultralcd.h"
extern bool Stopped;
// Reserve BUFSIZE lines of length MAX_CMD_SIZE plus CMDBUFFER_RESERVE_FRONT.
char cmdbuffer[BUFSIZE * (MAX_CMD_SIZE + 1) + CMDBUFFER_RESERVE_FRONT];
// Head of the circular buffer, where to read.
size_t bufindr = 0;
// Tail of the buffer, where to write.
static size_t bufindw = 0;
// Number of lines in cmdbuffer.
int buflen = 0;
// Flag for processing the current command inside the main Arduino loop().
// If a new command was pushed to the front of a command buffer while
// processing another command, this replaces the command on the top.
// Therefore don't remove the command from the queue in the loop() function.
bool cmdbuffer_front_already_processed = false;
int serial_count = 0; //index of character read from serial line
boolean comment_mode = false;
char *strchr_pointer; // just a pointer to find chars in the command string like X, Y, Z, E, etc
unsigned long TimeSent = _millis();
unsigned long TimeNow = _millis();
long gcode_N = 0;
long gcode_LastN = 0;
long Stopped_gcode_LastN = 0;
uint32_t sdpos_atomic = 0;
// Pop the currently processed command from the queue.
// It is expected, that there is at least one command in the queue.
bool cmdqueue_pop_front()
{
if (buflen > 0) {
#ifdef CMDBUFFER_DEBUG
SERIAL_ECHOPGM("Dequeing ");
SERIAL_ECHO(cmdbuffer+bufindr+CMDHDRSIZE);
SERIAL_ECHOLNPGM("");
SERIAL_ECHOPGM("Old indices: buflen ");
SERIAL_ECHO(buflen);
SERIAL_ECHOPGM(", bufindr ");
SERIAL_ECHO(bufindr);
SERIAL_ECHOPGM(", bufindw ");
SERIAL_ECHO(bufindw);
SERIAL_ECHOPGM(", serial_count ");
SERIAL_ECHO(serial_count);
SERIAL_ECHOPGM(", bufsize ");
SERIAL_ECHO(sizeof(cmdbuffer));
SERIAL_ECHOLNPGM("");
#endif /* CMDBUFFER_DEBUG */
if (-- buflen == 0) {
// Empty buffer.
if (serial_count == 0)
// No serial communication is pending. Reset both pointers to zero.
bufindw = 0;
bufindr = bufindw;
} else {
// There is at least one ready line in the buffer.
// First skip the current command ID and iterate up to the end of the string.
for (bufindr += CMDHDRSIZE; cmdbuffer[bufindr] != 0; ++ bufindr) ;
// Second, skip the end of string null character and iterate until a nonzero command ID is found.
for (++ bufindr; bufindr < sizeof(cmdbuffer) && cmdbuffer[bufindr] == 0; ++ bufindr) ;
// If the end of the buffer was empty,
if (bufindr == sizeof(cmdbuffer)) {
// skip to the start and find the nonzero command.
for (bufindr = 0; cmdbuffer[bufindr] == 0; ++ bufindr) ;
}
#ifdef CMDBUFFER_DEBUG
SERIAL_ECHOPGM("New indices: buflen ");
SERIAL_ECHO(buflen);
SERIAL_ECHOPGM(", bufindr ");
SERIAL_ECHO(bufindr);
SERIAL_ECHOPGM(", bufindw ");
SERIAL_ECHO(bufindw);
SERIAL_ECHOPGM(", serial_count ");
SERIAL_ECHO(serial_count);
SERIAL_ECHOPGM(" new command on the top: ");
SERIAL_ECHO(cmdbuffer+bufindr+CMDHDRSIZE);
SERIAL_ECHOLNPGM("");
#endif /* CMDBUFFER_DEBUG */
}
return true;
}
return false;
}
void cmdqueue_reset()
{
bufindr = 0;
bufindw = 0;
buflen = 0;
//commands are removed from command queue after process_command() function is finished
//reseting command queue and enqueing new commands during some (usually long running) command processing would cause that new commands are immediately removed from queue (or damaged)
//this will ensure that all new commands which are enqueued after cmdqueue reset, will be always executed
cmdbuffer_front_already_processed = true;
}
// How long a string could be pushed to the front of the command queue?
// If yes, adjust bufindr to the new position, where the new command could be enqued.
// len_asked does not contain the zero terminator size.
static bool cmdqueue_could_enqueue_front(size_t len_asked)
{
// MAX_CMD_SIZE has to accommodate the zero terminator.
if (len_asked >= MAX_CMD_SIZE)
return false;
// Remove the currently processed command from the queue.
if (! cmdbuffer_front_already_processed) {
cmdqueue_pop_front();
cmdbuffer_front_already_processed = true;
}
if (bufindr == bufindw && buflen > 0)
// Full buffer.
return false;
// Adjust the end of the write buffer based on whether a partial line is in the receive buffer.
int endw = (serial_count > 0) ? (bufindw + MAX_CMD_SIZE + 1) : bufindw;
if (bufindw < bufindr) {
int bufindr_new = bufindr - len_asked - (1 + CMDHDRSIZE);
// Simple case. There is a contiguous space between the write buffer and the read buffer.
if (endw <= bufindr_new) {
bufindr = bufindr_new;
return true;
}
} else {
// Otherwise the free space is split between the start and end.
if (len_asked + (1 + CMDHDRSIZE) <= bufindr) {
// Could fit at the start.
bufindr -= len_asked + (1 + CMDHDRSIZE);
return true;
}
int bufindr_new = sizeof(cmdbuffer) - len_asked - (1 + CMDHDRSIZE);
if (endw <= bufindr_new) {
memset(cmdbuffer, 0, bufindr);
bufindr = bufindr_new;
return true;
}
}
return false;
}
// Could one enqueue a command of length len_asked into the buffer,
// while leaving CMDBUFFER_RESERVE_FRONT at the start?
// If yes, adjust bufindw to the new position, where the new command could be enqued.
// len_asked does not contain the zero terminator size.
// This function may update bufindw, therefore for the power panic to work, this function must be called
// with the interrupts disabled!
static bool cmdqueue_could_enqueue_back(size_t len_asked, bool atomic_update = false)
{
// MAX_CMD_SIZE has to accommodate the zero terminator.
if (len_asked >= MAX_CMD_SIZE)
return false;
if (bufindr == bufindw && buflen > 0)
// Full buffer.
return false;
if (serial_count > 0) {
// If there is some data stored starting at bufindw, len_asked is certainly smaller than
// the allocated data buffer. Try to reserve a new buffer and to move the already received
// serial data.
// How much memory to reserve for the commands pushed to the front?
// End of the queue, when pushing to the end.
size_t endw = bufindw + len_asked + (1 + CMDHDRSIZE);
if (bufindw < bufindr)
// Simple case. There is a contiguous space between the write buffer and the read buffer.
return endw + CMDBUFFER_RESERVE_FRONT <= bufindr;
// Otherwise the free space is split between the start and end.
if (// Could one fit to the end, including the reserve?
endw + CMDBUFFER_RESERVE_FRONT <= sizeof(cmdbuffer) ||
// Could one fit to the end, and the reserve to the start?
(endw <= sizeof(cmdbuffer) && CMDBUFFER_RESERVE_FRONT <= bufindr))
return true;
// Could one fit both to the start?
if (len_asked + (1 + CMDHDRSIZE) + CMDBUFFER_RESERVE_FRONT <= bufindr) {
// Mark the rest of the buffer as used.
memset(cmdbuffer+bufindw, 0, sizeof(cmdbuffer)-bufindw);
// and point to the start.
// Be careful! The bufindw needs to be changed atomically for the power panic & filament panic to work.
if (atomic_update)
cli();
bufindw = 0;
if (atomic_update)
sei();
return true;
}
} else {
// How much memory to reserve for the commands pushed to the front?
// End of the queue, when pushing to the end.
size_t endw = bufindw + len_asked + (1 + CMDHDRSIZE);
if (bufindw < bufindr)
// Simple case. There is a contiguous space between the write buffer and the read buffer.
return endw + CMDBUFFER_RESERVE_FRONT <= bufindr;
// Otherwise the free space is split between the start and end.
if (// Could one fit to the end, including the reserve?
endw + CMDBUFFER_RESERVE_FRONT <= sizeof(cmdbuffer) ||
// Could one fit to the end, and the reserve to the start?
(endw <= sizeof(cmdbuffer) && CMDBUFFER_RESERVE_FRONT <= bufindr))
return true;
// Could one fit both to the start?
if (len_asked + (1 + CMDHDRSIZE) + CMDBUFFER_RESERVE_FRONT <= bufindr) {
// Mark the rest of the buffer as used.
memset(cmdbuffer+bufindw, 0, sizeof(cmdbuffer)-bufindw);
// and point to the start.
// Be careful! The bufindw needs to be changed atomically for the power panic & filament panic to work.
if (atomic_update)
cli();
bufindw = 0;
if (atomic_update)
sei();
return true;
}
}
return false;
}
#ifdef CMDBUFFER_DEBUG
void cmdqueue_dump_to_serial_single_line(int nr, const char *p)
{
SERIAL_ECHOPGM("Entry nr: ");
SERIAL_ECHO(nr);
SERIAL_ECHOPGM(", type: ");
int type = *p;
SERIAL_ECHO(type);
SERIAL_ECHOPGM(", size: ");
unsigned int size = *(unsigned int*)(p + 1);
SERIAL_ECHO(size);
SERIAL_ECHOPGM(", cmd: ");
SERIAL_ECHO(p + CMDHDRSIZE);
SERIAL_ECHOLNPGM("");
}
void cmdqueue_dump_to_serial()
{
if (buflen == 0) {
SERIAL_ECHOLNPGM("The command buffer is empty.");
} else {
SERIAL_ECHOPGM("Content of the buffer: entries ");
SERIAL_ECHO(buflen);
SERIAL_ECHOPGM(", indr ");
SERIAL_ECHO(bufindr);
SERIAL_ECHOPGM(", indw ");
SERIAL_ECHO(bufindw);
SERIAL_ECHOLNPGM("");
int nr = 0;
if (bufindr < bufindw) {
for (const char *p = cmdbuffer + bufindr; p < cmdbuffer + bufindw; ++ nr) {
cmdqueue_dump_to_serial_single_line(nr, p);
// Skip the command.
for (p += CMDHDRSIZE; *p != 0; ++ p);
// Skip the gaps.
for (++p; p < cmdbuffer + bufindw && *p == 0; ++ p);
}
} else {
for (const char *p = cmdbuffer + bufindr; p < cmdbuffer + sizeof(cmdbuffer); ++ nr) {
cmdqueue_dump_to_serial_single_line(nr, p);
// Skip the command.
for (p += CMDHDRSIZE; *p != 0; ++ p);
// Skip the gaps.
for (++p; p < cmdbuffer + sizeof(cmdbuffer) && *p == 0; ++ p);
}
for (const char *p = cmdbuffer; p < cmdbuffer + bufindw; ++ nr) {
cmdqueue_dump_to_serial_single_line(nr, p);
// Skip the command.
for (p += CMDHDRSIZE; *p != 0; ++ p);
// Skip the gaps.
for (++p; p < cmdbuffer + bufindw && *p == 0; ++ p);
}
}
SERIAL_ECHOLNPGM("End of the buffer.");
}
}
#endif /* CMDBUFFER_DEBUG */
//adds an command to the main command buffer
//thats really done in a non-safe way.
//needs overworking someday
// Currently the maximum length of a command piped through this function is around 20 characters
void enquecommand(const char *cmd, bool from_progmem)
{
size_t len = from_progmem ? strlen_P(cmd) : strlen(cmd);
// Does cmd fit the queue while leaving sufficient space at the front for the chained commands?
// If it fits, it may move bufindw, so it points to a contiguous buffer, which fits cmd.
if (cmdqueue_could_enqueue_back(len)) {
// This is dangerous if a mixing of serial and this happens
// This may easily be tested: If serial_count > 0, we have a problem.
cmdbuffer[bufindw] = CMDBUFFER_CURRENT_TYPE_UI;
if (from_progmem)
strcpy_P(cmdbuffer + bufindw + CMDHDRSIZE, cmd);
else
strcpy(cmdbuffer + bufindw + CMDHDRSIZE, cmd);
SERIAL_ECHO_START;
SERIAL_ECHORPGM(MSG_Enqueing);
SERIAL_ECHO(cmdbuffer + bufindw + CMDHDRSIZE);
SERIAL_ECHOLNPGM("\"");
bufindw += len + (CMDHDRSIZE + 1);
if (bufindw == sizeof(cmdbuffer))
bufindw = 0;
++ buflen;
#ifdef CMDBUFFER_DEBUG
cmdqueue_dump_to_serial();
#endif /* CMDBUFFER_DEBUG */
} else {
SERIAL_ERROR_START;
SERIAL_ECHORPGM(MSG_Enqueing);
if (from_progmem)
SERIAL_PROTOCOLRPGM(cmd);
else
SERIAL_ECHO(cmd);
SERIAL_ECHOLNPGM("\" failed: Buffer full!");
#ifdef CMDBUFFER_DEBUG
cmdqueue_dump_to_serial();
#endif /* CMDBUFFER_DEBUG */
}
}
bool cmd_buffer_empty()
{
return (buflen == 0);
}
void enquecommand_front(const char *cmd, bool from_progmem)
{
size_t len = from_progmem ? strlen_P(cmd) : strlen(cmd);
// Does cmd fit the queue? This call shall move bufindr, so the command may be copied.
if (cmdqueue_could_enqueue_front(len)) {
cmdbuffer[bufindr] = CMDBUFFER_CURRENT_TYPE_UI;
if (from_progmem)
strcpy_P(cmdbuffer + bufindr + CMDHDRSIZE, cmd);
else
strcpy(cmdbuffer + bufindr + CMDHDRSIZE, cmd);
++ buflen;
SERIAL_ECHO_START;
SERIAL_ECHOPGM("Enqueing to the front: \"");
SERIAL_ECHO(cmdbuffer + bufindr + CMDHDRSIZE);
SERIAL_ECHOLNPGM("\"");
#ifdef CMDBUFFER_DEBUG
cmdqueue_dump_to_serial();
#endif /* CMDBUFFER_DEBUG */
} else {
SERIAL_ERROR_START;
SERIAL_ECHOPGM("Enqueing to the front: \"");
if (from_progmem)
SERIAL_PROTOCOLRPGM(cmd);
else
SERIAL_ECHO(cmd);
SERIAL_ECHOLNPGM("\" failed: Buffer full!");
#ifdef CMDBUFFER_DEBUG
cmdqueue_dump_to_serial();
#endif /* CMDBUFFER_DEBUG */
}
}
// Mark the command at the top of the command queue as new.
// Therefore it will not be removed from the queue.
void repeatcommand_front()
{
cmdbuffer_front_already_processed = true;
}
bool is_buffer_empty()
{
if (buflen == 0) return true;
else return false;
}
void proc_commands() {
if (buflen)
{
process_commands();
if (!cmdbuffer_front_already_processed)
cmdqueue_pop_front();
cmdbuffer_front_already_processed = false;
}
}
void get_command()
{
// Test and reserve space for the new command string.
if (! cmdqueue_could_enqueue_back(MAX_CMD_SIZE - 1, true))
return;
if (MYSERIAL.available() == RX_BUFFER_SIZE - 1) { //compare number of chars buffered in rx buffer with rx buffer size
MYSERIAL.flush();
SERIAL_ECHOLNPGM("Full RX Buffer"); //if buffer was full, there is danger that reading of last gcode will not be completed
}
// start of serial line processing loop
while ((MYSERIAL.available() > 0 && !saved_printing) || (MYSERIAL.available() > 0 && isPrintPaused)) { //is print is saved (crash detection or filament detection), dont process data from serial line
char serial_char = MYSERIAL.read();
/* if (selectedSerialPort == 1)
{
selectedSerialPort = 0;
MYSERIAL.write(serial_char); // for debuging serial line 2 in farm_mode
selectedSerialPort = 1;
} */ //RP - removed
TimeSent = _millis();
TimeNow = _millis();
if (serial_char < 0)
// Ignore extended ASCII characters. These characters have no meaning in the G-code apart from the file names
// and Marlin does not support such file names anyway.
// Serial characters with a highest bit set to 1 are generated when the USB cable is unplugged, leading
// to a hang-up of the print process from an SD card.
continue;
if(serial_char == '\n' ||
serial_char == '\r' ||
serial_count >= (MAX_CMD_SIZE - 1) )
{
if(!serial_count) { //if empty line
comment_mode = false; //for new command
return;
}
cmdbuffer[bufindw+serial_count+CMDHDRSIZE] = 0; //terminate string
if(!comment_mode){
gcode_N = 0;
// Line numbers must be first in buffer
if ((strstr(cmdbuffer+bufindw+CMDHDRSIZE, "PRUSA") == NULL) &&
(cmdbuffer[bufindw+CMDHDRSIZE] == 'N')) {
// Line number met. When sending a G-code over a serial line, each line may be stamped with its index,
// and Marlin tests, whether the successive lines are stamped with an increasing line number ID
gcode_N = (strtol(cmdbuffer+bufindw+CMDHDRSIZE+1, NULL, 10));
if(gcode_N != gcode_LastN+1 && (strstr_P(cmdbuffer+bufindw+CMDHDRSIZE, PSTR("M110")) == NULL) ) {
// M110 - set current line number.
// Line numbers not sent in succession.
SERIAL_ERROR_START;
SERIAL_ERRORRPGM(_n("Line Number is not Last Line Number+1, Last Line: "));////MSG_ERR_LINE_NO
SERIAL_ERRORLN(gcode_LastN);
//Serial.println(gcode_N);
FlushSerialRequestResend();
serial_count = 0;
return;
}
if((strchr_pointer = strchr(cmdbuffer+bufindw+CMDHDRSIZE, '*')) != NULL)
{
byte checksum = 0;
char *p = cmdbuffer+bufindw+CMDHDRSIZE;
while (p != strchr_pointer)
checksum = checksum^(*p++);
if (int(strtol(strchr_pointer+1, NULL, 10)) != int(checksum)) {
SERIAL_ERROR_START;
SERIAL_ERRORRPGM(_n("checksum mismatch, Last Line: "));////MSG_ERR_CHECKSUM_MISMATCH
SERIAL_ERRORLN(gcode_LastN);
FlushSerialRequestResend();
serial_count = 0;
return;
}
// If no errors, remove the checksum and continue parsing.
*strchr_pointer = 0;
}
else
{
SERIAL_ERROR_START;
SERIAL_ERRORRPGM(_n("No Checksum with line number, Last Line: "));////MSG_ERR_NO_CHECKSUM
SERIAL_ERRORLN(gcode_LastN);
FlushSerialRequestResend();
serial_count = 0;
return;
}
// Don't parse N again with code_seen('N')
cmdbuffer[bufindw + CMDHDRSIZE] = '$';
//if no errors, continue parsing
gcode_LastN = gcode_N;
}
// if we don't receive 'N' but still see '*'
if ((cmdbuffer[bufindw + CMDHDRSIZE] != 'N') && (cmdbuffer[bufindw + CMDHDRSIZE] != '$') && (strchr(cmdbuffer+bufindw+CMDHDRSIZE, '*') != NULL))
{
SERIAL_ERROR_START;
SERIAL_ERRORRPGM(_n("No Line Number with checksum, Last Line: "));////MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM
SERIAL_ERRORLN(gcode_LastN);
FlushSerialRequestResend();
serial_count = 0;
return;
}
if ((strchr_pointer = strchr(cmdbuffer+bufindw+CMDHDRSIZE, 'G')) != NULL) {
if (! IS_SD_PRINTING) {
usb_printing_counter = 10;
is_usb_printing = true;
}
if (Stopped == true) {
int gcode = strtol(strchr_pointer+1, NULL, 10);
if (gcode >= 0 && gcode <= 3) {
SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED);
LCD_MESSAGERPGM(_T(MSG_STOPPED));
}
}
} // end of 'G' command
//If command was e-stop process now
if(strcmp(cmdbuffer+bufindw+CMDHDRSIZE, "M112") == 0)
kill("", 2);
// Store the current line into buffer, move to the next line.
// Store type of entry
cmdbuffer[bufindw] = gcode_N ? CMDBUFFER_CURRENT_TYPE_USB_WITH_LINENR : CMDBUFFER_CURRENT_TYPE_USB;
#ifdef CMDBUFFER_DEBUG
SERIAL_ECHO_START;
SERIAL_ECHOPGM("Storing a command line to buffer: ");
SERIAL_ECHO(cmdbuffer+bufindw+CMDHDRSIZE);
SERIAL_ECHOLNPGM("");
#endif /* CMDBUFFER_DEBUG */
bufindw += strlen(cmdbuffer+bufindw+CMDHDRSIZE) + (1 + CMDHDRSIZE);
if (bufindw == sizeof(cmdbuffer))
bufindw = 0;
++ buflen;
#ifdef CMDBUFFER_DEBUG
SERIAL_ECHOPGM("Number of commands in the buffer: ");
SERIAL_ECHO(buflen);
SERIAL_ECHOLNPGM("");
#endif /* CMDBUFFER_DEBUG */
} // end of 'not comment mode'
serial_count = 0; //clear buffer
// Don't call cmdqueue_could_enqueue_back if there are no characters waiting
// in the queue, as this function will reserve the memory.
if (MYSERIAL.available() == 0 || ! cmdqueue_could_enqueue_back(MAX_CMD_SIZE-1, true))
return;
} // end of "end of line" processing
else {
// Not an "end of line" symbol. Store the new character into a buffer.
if(serial_char == ';') comment_mode = true;
if(!comment_mode) cmdbuffer[bufindw+CMDHDRSIZE+serial_count++] = serial_char;
}
} // end of serial line processing loop
if(farm_mode){
TimeNow = _millis();
if ( ((TimeNow - TimeSent) > 800) && (serial_count > 0) ) {
cmdbuffer[bufindw+serial_count+CMDHDRSIZE] = 0;
bufindw += strlen(cmdbuffer+bufindw+CMDHDRSIZE) + (1 + CMDHDRSIZE);
if (bufindw == sizeof(cmdbuffer))
bufindw = 0;
++ buflen;
serial_count = 0;
SERIAL_ECHOPGM("TIMEOUT:");
//memset(cmdbuffer, 0 , sizeof(cmdbuffer));
return;
}
}
#ifdef SDSUPPORT
if(!card.sdprinting || serial_count!=0){
// If there is a half filled buffer from serial line, wait until return before
// continuing with the serial line.
return;
}
//'#' stops reading from SD to the buffer prematurely, so procedural macro calls are possible
// if it occurs, stop_buffering is triggered and the buffer is ran dry.
// this character _can_ occur in serial com, due to checksums. however, no checksums are used in SD printing
static bool stop_buffering=false;
if(buflen==0) stop_buffering=false;
union {
struct {
char lo;
char hi;
} lohi;
uint16_t value;
} sd_count;
sd_count.value = 0;
// Reads whole lines from the SD card. Never leaves a half-filled line in the cmdbuffer.
while( !card.eof() && !stop_buffering) {
int16_t n=card.get();
char serial_char = (char)n;
if(serial_char == '\n' ||
serial_char == '\r' ||
((serial_char == '#' || serial_char == ':') && comment_mode == false) ||
serial_count >= (MAX_CMD_SIZE - 1) || n==-1)
{
if(card.eof()){
SERIAL_PROTOCOLLNRPGM(_n("Done printing file"));////MSG_FILE_PRINTED
stoptime=_millis();
char time[30];
unsigned long t=(stoptime-starttime-pause_time)/1000;
pause_time = 0;
int hours, minutes;
minutes=(t/60)%60;
hours=t/60/60;
save_statistics(total_filament_used, t);
sprintf_P(time, PSTR("%i hours %i minutes"),hours, minutes);
SERIAL_ECHO_START;
SERIAL_ECHOLN(time);
lcd_setstatus(time);
card.printingHasFinished();
card.checkautostart(true);
if (farm_mode)
{
prusa_statistics(6);
lcd_commands_type = LcdCommands::FarmModeConfirm;
}
}
if(serial_char=='#')
stop_buffering=true;
if(!serial_count)
{
// This is either an empty line, or a line with just a comment.
// Continue to the following line, and continue accumulating the number of bytes
// read from the sdcard into sd_count,
// so that the lenght of the already read empty lines and comments will be added
// to the following non-empty line.
comment_mode = false;
continue; //if empty line
}
// The new command buffer could be updated non-atomically, because it is not yet considered
// to be inside the active queue.
sd_count.value = (card.get_sdpos()+1) - sdpos_atomic;
cmdbuffer[bufindw] = CMDBUFFER_CURRENT_TYPE_SDCARD;
cmdbuffer[bufindw+1] = sd_count.lohi.lo;
cmdbuffer[bufindw+2] = sd_count.lohi.hi;
cmdbuffer[bufindw+serial_count+CMDHDRSIZE] = 0; //terminate string
// Calculate the length before disabling the interrupts.
uint8_t len = strlen(cmdbuffer+bufindw+CMDHDRSIZE) + (1 + CMDHDRSIZE);
// SERIAL_ECHOPGM("SD cmd(");
// MYSERIAL.print(sd_count.value, DEC);
// SERIAL_ECHOPGM(") ");
// SERIAL_ECHOLN(cmdbuffer+bufindw+CMDHDRSIZE);
// SERIAL_ECHOPGM("cmdbuffer:");
// MYSERIAL.print(cmdbuffer);
// SERIAL_ECHOPGM("buflen:");
// MYSERIAL.print(buflen+1);
sd_count.value = 0;
cli();
// This block locks the interrupts globally for 3.56 us,
// which corresponds to a maximum repeat frequency of 280.70 kHz.
// This blocking is safe in the context of a 10kHz stepper driver interrupt
// or a 115200 Bd serial line receive interrupt, which will not trigger faster than 12kHz.
++ buflen;
bufindw += len;
sdpos_atomic = card.get_sdpos()+1;
if (bufindw == sizeof(cmdbuffer))
bufindw = 0;
sei();
comment_mode = false; //for new command
serial_count = 0; //clear buffer
// The following line will reserve buffer space if available.
if (! cmdqueue_could_enqueue_back(MAX_CMD_SIZE-1, true))
return;
}
else
{
if(serial_char == ';') comment_mode = true;
else if(!comment_mode) cmdbuffer[bufindw+CMDHDRSIZE+serial_count++] = serial_char;
}
}
#endif //SDSUPPORT
}
uint16_t cmdqueue_calc_sd_length()
{
if (buflen == 0)
return 0;
union {
struct {
char lo;
char hi;
} lohi;
uint16_t value;
} sdlen_single;
uint16_t sdlen = 0;
for (size_t _buflen = buflen, _bufindr = bufindr;;) {
if (cmdbuffer[_bufindr] == CMDBUFFER_CURRENT_TYPE_SDCARD) {
sdlen_single.lohi.lo = cmdbuffer[_bufindr + 1];
sdlen_single.lohi.hi = cmdbuffer[_bufindr + 2];
sdlen += sdlen_single.value;
}
if (-- _buflen == 0)
break;
// First skip the current command ID and iterate up to the end of the string.
for (_bufindr += CMDHDRSIZE; cmdbuffer[_bufindr] != 0; ++ _bufindr) ;
// Second, skip the end of string null character and iterate until a nonzero command ID is found.
for (++ _bufindr; _bufindr < sizeof(cmdbuffer) && cmdbuffer[_bufindr] == 0; ++ _bufindr) ;
// If the end of the buffer was empty,
if (_bufindr == sizeof(cmdbuffer)) {
// skip to the start and find the nonzero command.
for (_bufindr = 0; cmdbuffer[_bufindr] == 0; ++ _bufindr) ;
}
}
return sdlen;
}

94
Firmware/cmdqueue.h Normal file
View File

@ -0,0 +1,94 @@
#ifndef CMDQUEUE_H
#define CMDQUEUE_H
#include "Marlin.h"
#include "language.h"
// String circular buffer. Commands may be pushed to the buffer from both sides:
// Chained commands will be pushed to the front, interactive (from LCD menu)
// and printing commands (from serial line or from SD card) are pushed to the tail.
// First character of each entry indicates the type of the entry:
#define CMDBUFFER_CURRENT_TYPE_UNKNOWN 0
// Command in cmdbuffer was sent over USB.
#define CMDBUFFER_CURRENT_TYPE_USB 1
// Command in cmdbuffer was read from SDCARD.
#define CMDBUFFER_CURRENT_TYPE_SDCARD 2
// Command in cmdbuffer was generated by the UI.
#define CMDBUFFER_CURRENT_TYPE_UI 3
// Command in cmdbuffer was generated by another G-code.
#define CMDBUFFER_CURRENT_TYPE_CHAINED 4
// Command has been processed and its SD card length has been possibly pushed
// to the planner queue, but not yet removed from the cmdqueue.
// This is a temporary state to reduce stepper interrupt locking time.
#define CMDBUFFER_CURRENT_TYPE_TO_BE_REMOVED 5
//Command in cmdbuffer was sent over USB and contains line number
#define CMDBUFFER_CURRENT_TYPE_USB_WITH_LINENR 6
// How much space to reserve for the chained commands
// of type CMDBUFFER_CURRENT_TYPE_CHAINED,
// which are pushed to the front of the queue?
// Maximum 5 commands of max length 20 + null terminator.
#define CMDBUFFER_RESERVE_FRONT (5*21)
extern char cmdbuffer[BUFSIZE * (MAX_CMD_SIZE + 1) + CMDBUFFER_RESERVE_FRONT];
extern size_t bufindr;
extern int buflen;
extern bool cmdbuffer_front_already_processed;
// Type of a command, which is to be executed right now.
#define CMDBUFFER_CURRENT_TYPE (cmdbuffer[bufindr])
// String of a command, which is to be executed right now.
#define CMDBUFFER_CURRENT_STRING (cmdbuffer+bufindr+CMDHDRSIZE)
// Enable debugging of the command buffer.
// Debugging information will be sent to serial line.
//#define CMDBUFFER_DEBUG
extern uint32_t sdpos_atomic;
extern int serial_count;
extern boolean comment_mode;
extern char *strchr_pointer;
extern unsigned long TimeSent;
extern unsigned long TimeNow;
extern long gcode_N;
extern long gcode_LastN;
extern long Stopped_gcode_LastN;
extern bool cmdqueue_pop_front();
extern void cmdqueue_reset();
#ifdef CMDBUFFER_DEBUG
extern void cmdqueue_dump_to_serial_single_line(int nr, const char *p);
extern void cmdqueue_dump_to_serial();
#endif /* CMDBUFFER_DEBUG */
extern bool cmd_buffer_empty();
extern void enquecommand(const char *cmd, bool from_progmem);
extern void enquecommand_front(const char *cmd, bool from_progmem);
extern void repeatcommand_front();
extern bool is_buffer_empty();
extern void get_command();
extern uint16_t cmdqueue_calc_sd_length();
// Return True if a character was found
static inline bool code_seen(char code) { return (strchr_pointer = strchr(CMDBUFFER_CURRENT_STRING, code)) != NULL; }
static inline bool code_seen(const char *code) { return (strchr_pointer = strstr(CMDBUFFER_CURRENT_STRING, code)) != NULL; }
static inline float code_value() { return strtod(strchr_pointer+1, NULL);}
static inline long code_value_long() { return strtol(strchr_pointer+1, NULL, 10); }
static inline int16_t code_value_short() { return int16_t(strtol(strchr_pointer+1, NULL, 10)); };
static inline uint8_t code_value_uint8() { return uint8_t(strtol(strchr_pointer+1, NULL, 10)); };
static inline float code_value_float()
{
char* e = strchr(strchr_pointer, 'E');
if (!e) return strtod(strchr_pointer + 1, NULL);
*e = 0;
float ret = strtod(strchr_pointer + 1, NULL);
*e = 'E';
return ret;
}
#endif //CMDQUEUE_H

55
Firmware/config.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef _CONFIG_H
#define _CONFIG_H
//ADC configuration
#define ADC_CHAN_MSK 0b0000001001011111 //used AD channels bit mask (0,1,2,3,4,6,9)
#define ADC_CHAN_CNT 7 //number of used channels)
#define ADC_OVRSAMPL 16 //oversampling multiplier
#define ADC_CALLBACK adc_ready //callback function ()
//SWI2C configuration
#define SWI2C
//#define SWI2C_SDA 20 //SDA on P3
//#define SWI2C_SCL 21 //SCL on P3
#define SWI2C_A8
#define SWI2C_DEL 20 //2us clock delay
#define SWI2C_TMO 2048 //2048 cycles timeout
//PAT9125 configuration
#define PAT9125_SWI2C
#define PAT9125_I2C_ADDR 0x75 //ID=LO
//#define PAT9125_I2C_ADDR 0x79 //ID=HI
//#define PAT9125_I2C_ADDR 0x73 //ID=NC
#define PAT9125_XRES 0
#define PAT9125_YRES 240
//SM4 configuration
#define SM4_DEFDELAY 500 //default step delay [us]
//TMC2130 - Trinamic stepper driver
//pinout - hardcoded
//spi:
#define TMC2130_SPI_RATE 0 // fosc/4 = 4MHz
#define TMC2130_SPCR SPI_SPCR(TMC2130_SPI_RATE, 1, 1, 1, 0)
#define TMC2130_SPSR SPI_SPSR(TMC2130_SPI_RATE)
//W25X20CL configuration
//pinout:
#define W25X20CL_PIN_CS 32
//spi:
#define W25X20CL_SPI_RATE 0 // fosc/4 = 4MHz
#define W25X20CL_SPCR SPI_SPCR(W25X20CL_SPI_RATE, 1, 1, 1, 0)
#define W25X20CL_SPSR SPI_SPSR(W25X20CL_SPI_RATE)
#include "boards.h"
#include "Configuration_prusa.h"
//LANG - Multi-language support
//#define LANG_MODE 0 // primary language only
#define LANG_MODE 1 // sec. language support
#define LANG_SIZE_RESERVED 0x3000 // reserved space for secondary language (12288 bytes)
#endif //_CONFIG_H

292
Firmware/conv2str.cpp Normal file
View File

@ -0,0 +1,292 @@
//conv2str.cpp - Float conversion utilities
#include "conv2str.h"
#include <stdlib.h>
// convert float to string with +123.4 format
char conv[8];
char *ftostr3(const float &x)
{
return itostr3((int)x);
}
char *itostr2(const uint8_t &x)
{
//sprintf(conv,"%5.1f",x);
int xx = x;
conv[0] = (xx / 10) % 10 + '0';
conv[1] = (xx) % 10 + '0';
conv[2] = 0;
return conv;
}
// Convert float to string with 123.4 format, dropping sign
char *ftostr31(const float &x)
{
int xx = x * 10;
conv[0] = (xx >= 0) ? '+' : '-';
xx = abs(xx);
conv[1] = (xx / 1000) % 10 + '0';
conv[2] = (xx / 100) % 10 + '0';
conv[3] = (xx / 10) % 10 + '0';
conv[4] = '.';
conv[5] = (xx) % 10 + '0';
conv[6] = 0;
return conv;
}
// Convert float to string with 123.4 format
char *ftostr31ns(const float &x)
{
int xx = x * 10;
//conv[0]=(xx>=0)?'+':'-';
xx = abs(xx);
conv[0] = (xx / 1000) % 10 + '0';
conv[1] = (xx / 100) % 10 + '0';
conv[2] = (xx / 10) % 10 + '0';
conv[3] = '.';
conv[4] = (xx) % 10 + '0';
conv[5] = 0;
return conv;
}
char *ftostr32(const float &x)
{
long xx = x * 100;
if (xx >= 0)
conv[0] = (xx / 10000) % 10 + '0';
else
conv[0] = '-';
xx = abs(xx);
conv[1] = (xx / 1000) % 10 + '0';
conv[2] = (xx / 100) % 10 + '0';
conv[3] = '.';
conv[4] = (xx / 10) % 10 + '0';
conv[5] = (xx) % 10 + '0';
conv[6] = 0;
return conv;
}
//// Convert float to rj string with 123.45 format
char *ftostr32ns(const float &x) {
long xx = abs(x);
conv[0] = xx >= 10000 ? (xx / 10000) % 10 + '0' : ' ';
conv[1] = xx >= 1000 ? (xx / 1000) % 10 + '0' : ' ';
conv[2] = xx >= 100 ? (xx / 100) % 10 + '0' : '0';
conv[3] = '.';
conv[4] = (xx / 10) % 10 + '0';
conv[5] = xx % 10 + '0';
return conv;
}
// Convert float to string with 1.234 format
char *ftostr43(const float &x, uint8_t offset)
{
const size_t maxOffset = sizeof(conv)/sizeof(conv[0]) - 6;
if (offset>maxOffset) offset = maxOffset;
long xx = x * 1000;
if (xx >= 0)
conv[offset] = (xx / 1000) % 10 + '0';
else
conv[offset] = '-';
xx = abs(xx);
conv[offset + 1] = '.';
conv[offset + 2] = (xx / 100) % 10 + '0';
conv[offset + 3] = (xx / 10) % 10 + '0';
conv[offset + 4] = (xx) % 10 + '0';
conv[offset + 5] = 0;
return conv;
}
//Float to string with 1.23 format
char *ftostr12ns(const float &x)
{
long xx = x * 100;
xx = abs(xx);
conv[0] = (xx / 100) % 10 + '0';
conv[1] = '.';
conv[2] = (xx / 10) % 10 + '0';
conv[3] = (xx) % 10 + '0';
conv[4] = 0;
return conv;
}
//Float to string with 1.234 format
char *ftostr13ns(const float &x)
{
long xx = x * 1000;
if (xx >= 0)
conv[0] = ' ';
else
conv[0] = '-';
xx = abs(xx);
conv[1] = (xx / 1000) % 10 + '0';
conv[2] = '.';
conv[3] = (xx / 100) % 10 + '0';
conv[4] = (xx / 10) % 10 + '0';
conv[5] = (xx) % 10 + '0';
conv[6] = 0;
return conv;
}
// convert float to space-padded string with -_23.4_ format
char *ftostr32sp(const float &x) {
long xx = abs(x * 100);
uint8_t dig;
if (x < 0) { // negative val = -_0
conv[0] = '-';
dig = (xx / 1000) % 10;
conv[1] = dig ? '0' + dig : ' ';
}
else { // positive val = __0
dig = (xx / 10000) % 10;
if (dig) {
conv[0] = '0' + dig;
conv[1] = '0' + (xx / 1000) % 10;
}
else {
conv[0] = ' ';
dig = (xx / 1000) % 10;
conv[1] = dig ? '0' + dig : ' ';
}
}
conv[2] = '0' + (xx / 100) % 10; // lsd always
dig = xx % 10;
if (dig) { // 2 decimal places
conv[5] = '0' + dig;
conv[4] = '0' + (xx / 10) % 10;
conv[3] = '.';
}
else { // 1 or 0 decimal place
dig = (xx / 10) % 10;
if (dig) {
conv[4] = '0' + dig;
conv[3] = '.';
}
else {
conv[3] = conv[4] = ' ';
}
conv[5] = ' ';
}
conv[6] = '\0';
return conv;
}
char *itostr31(const int &xx)
{
conv[0] = (xx >= 0) ? '+' : '-';
conv[1] = (xx / 1000) % 10 + '0';
conv[2] = (xx / 100) % 10 + '0';
conv[3] = (xx / 10) % 10 + '0';
conv[4] = '.';
conv[5] = (xx) % 10 + '0';
conv[6] = 0;
return conv;
}
// Convert int to rj string with 123 or -12 format
char *itostr3(const int &x)
{
int xx = x;
if (xx < 0) {
conv[0] = '-';
xx = -xx;
} else if (xx >= 100)
conv[0] = (xx / 100) % 10 + '0';
else
conv[0] = ' ';
if (xx >= 10)
conv[1] = (xx / 10) % 10 + '0';
else
conv[1] = ' ';
conv[2] = (xx) % 10 + '0';
conv[3] = 0;
return conv;
}
// Convert int to lj string with 123 format
char *itostr3left(const int &xx)
{
if (xx >= 100)
{
conv[0] = (xx / 100) % 10 + '0';
conv[1] = (xx / 10) % 10 + '0';
conv[2] = (xx) % 10 + '0';
conv[3] = 0;
}
else if (xx >= 10)
{
conv[0] = (xx / 10) % 10 + '0';
conv[1] = (xx) % 10 + '0';
conv[2] = 0;
}
else
{
conv[0] = (xx) % 10 + '0';
conv[1] = 0;
}
return conv;
}
// Convert int to rj string with 1234 format
char *itostr4(const int &xx) {
conv[0] = xx >= 1000 ? (xx / 1000) % 10 + '0' : ' ';
conv[1] = xx >= 100 ? (xx / 100) % 10 + '0' : ' ';
conv[2] = xx >= 10 ? (xx / 10) % 10 + '0' : ' ';
conv[3] = xx % 10 + '0';
conv[4] = 0;
return conv;
}
// Convert float to rj string with 12345 format
char *ftostr5(const float &x) {
long xx = abs(x);
conv[0] = xx >= 10000 ? (xx / 10000) % 10 + '0' : ' ';
conv[1] = xx >= 1000 ? (xx / 1000) % 10 + '0' : ' ';
conv[2] = xx >= 100 ? (xx / 100) % 10 + '0' : ' ';
conv[3] = xx >= 10 ? (xx / 10) % 10 + '0' : ' ';
conv[4] = xx % 10 + '0';
conv[5] = 0;
return conv;
}
// Convert float to string with +1234.5 format
char *ftostr51(const float &x)
{
long xx = x * 10;
conv[0] = (xx >= 0) ? '+' : '-';
xx = abs(xx);
conv[1] = (xx / 10000) % 10 + '0';
conv[2] = (xx / 1000) % 10 + '0';
conv[3] = (xx / 100) % 10 + '0';
conv[4] = (xx / 10) % 10 + '0';
conv[5] = '.';
conv[6] = (xx) % 10 + '0';
conv[7] = 0;
return conv;
}
// Convert float to string with +123.45 format
char *ftostr52(const float &x)
{
long xx = x * 100;
conv[0] = (xx >= 0) ? '+' : '-';
xx = abs(xx);
conv[1] = (xx / 10000) % 10 + '0';
conv[2] = (xx / 1000) % 10 + '0';
conv[3] = (xx / 100) % 10 + '0';
conv[4] = '.';
conv[5] = (xx / 10) % 10 + '0';
conv[6] = (xx) % 10 + '0';
conv[7] = 0;
return conv;
}

28
Firmware/conv2str.h Normal file
View File

@ -0,0 +1,28 @@
//conv2str.h - Float conversion utilities
#ifndef _CONV2STR_H
#define _CONV2STR_H
#include <inttypes.h>
char *itostr2(const uint8_t &x);
char *itostr31(const int &xx);
char *itostr3(const int &xx);
char *itostr3left(const int &xx);
char *itostr4(const int &xx);
char *ftostr3(const float &x);
char *ftostr31ns(const float &x); // float to string without sign character
char *ftostr31(const float &x);
char *ftostr32(const float &x);
char *ftostr32ns(const float &x);
char *ftostr43(const float &x, uint8_t offset = 0);
char *ftostr12ns(const float &x);
char *ftostr13ns(const float &x);
char *ftostr32sp(const float &x); // remove zero-padding from ftostr32
char *ftostr5(const float &x);
char *ftostr51(const float &x);
char *ftostr52(const float &x);
#endif //_CONV2STR_H

2494
Firmware/doxyfile Normal file

File diff suppressed because it is too large Load Diff

187
Firmware/eeprom.cpp Normal file
View File

@ -0,0 +1,187 @@
//! @file
//! @date Jun 20, 2019
//! @author Marek Běl
#include "eeprom.h"
#include "Marlin.h"
#include <avr/eeprom.h>
#include <stdint.h>
#include "language.h"
#if 0
template <typename T>
static T eeprom_read(T *address);
template<>
char eeprom_read<char>(char *address)
{
return eeprom_read_byte(reinterpret_cast<uint8_t*>(address));
}
#endif
template <typename T>
static void eeprom_write(T *address, T value);
template<>
void eeprom_write<char>(char *addres, char value)
{
eeprom_write_byte(reinterpret_cast<uint8_t*>(addres), static_cast<uint8_t>(value));
}
template <typename T>
static bool eeprom_is_uninitialized(T *address);
template <>
bool eeprom_is_uninitialized<char>(char *address)
{
return (0xff == eeprom_read_byte(reinterpret_cast<uint8_t*>(address)));
}
bool eeprom_is_sheet_initialized(uint8_t sheet_num)
{
return (0xffff != eeprom_read_word(reinterpret_cast<uint16_t*>(&(EEPROM_Sheets_base->
s[sheet_num].z_offset))));
}
void eeprom_init()
{
if (eeprom_read_byte((uint8_t*)EEPROM_POWER_COUNT) == 0xff) eeprom_write_byte((uint8_t*)EEPROM_POWER_COUNT, 0);
if (eeprom_read_byte((uint8_t*)EEPROM_CRASH_COUNT_X) == 0xff) eeprom_write_byte((uint8_t*)EEPROM_CRASH_COUNT_X, 0);
if (eeprom_read_byte((uint8_t*)EEPROM_CRASH_COUNT_Y) == 0xff) eeprom_write_byte((uint8_t*)EEPROM_CRASH_COUNT_Y, 0);
if (eeprom_read_byte((uint8_t*)EEPROM_FERROR_COUNT) == 0xff) eeprom_write_byte((uint8_t*)EEPROM_FERROR_COUNT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_POWER_COUNT_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_FERROR_COUNT_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_MMU_FAIL_TOT) == 0xffff) eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT) == 0xffff) eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0);
if (eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
if (eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
if (eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet)) == EEPROM_EMPTY_VALUE)
{
eeprom_update_byte(&(EEPROM_Sheets_base->active_sheet), 0);
// When upgrading from version older version (before multiple sheets were implemented in v3.8.0)
// Sheet 1 uses the previous Live adjust Z (@EEPROM_BABYSTEP_Z)
int last_babystep = eeprom_read_word((uint16_t *)EEPROM_BABYSTEP_Z);
eeprom_update_word(reinterpret_cast<uint16_t *>(&(EEPROM_Sheets_base->s[0].z_offset)), last_babystep);
}
for (uint_least8_t i = 0; i < (sizeof(Sheets::s)/sizeof(Sheets::s[0])); ++i)
{
bool is_uninitialized = true;
for (uint_least8_t j = 0; j < (sizeof(Sheet::name)/sizeof(Sheet::name[0])); ++j)
{
if (!eeprom_is_uninitialized(&(EEPROM_Sheets_base->s[i].name[j]))) is_uninitialized = false;
}
if(is_uninitialized)
{
SheetName sheetName;
eeprom_default_sheet_name(i,sheetName);
for (uint_least8_t a = 0; a < sizeof(Sheet::name); ++a){
eeprom_write(&(EEPROM_Sheets_base->s[i].name[a]), sheetName.c[a]);
}
}
}
if(!eeprom_is_sheet_initialized(eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet))))
{
eeprom_switch_to_next_sheet();
}
check_babystep();
}
//! @brief Get default sheet name for index
//!
//! | index | sheetName |
//! | ----- | --------- |
//! | 0 | Smooth1 |
//! | 1 | Smooth2 |
//! | 2 | Textur1 |
//! | 3 | Textur2 |
//! | 4 | Custom1 |
//! | 5 | Custom2 |
//! | 6 | Custom3 |
//! | 7 | Custom4 |
//!
//! @param[in] index
//! @param[out] sheetName
void eeprom_default_sheet_name(uint8_t index, SheetName &sheetName)
{
static_assert(8 == sizeof(SheetName),"Default sheet name needs to be adjusted.");
if (index < 2)
{
strcpy_P(sheetName.c, PSTR("Smooth"));
}
else if (index < 4)
{
strcpy_P(sheetName.c, PSTR("Textur"));
}
else
{
strcpy_P(sheetName.c, PSTR("Custom"));
}
switch (index)
{
case 0:
sheetName.c[6] = '1';
break;
case 1:
sheetName.c[6] = '2';
break;
case 2:
sheetName.c[6] = '1';
break;
case 3:
sheetName.c[6] = '2';
break;
case 4:
sheetName.c[6] = '1';
break;
case 5:
sheetName.c[6] = '2';
break;
case 6:
sheetName.c[6] = '3';
break;
case 7:
sheetName.c[6] = '4';
break;
default:
break;
}
sheetName.c[7] = '\0';
}
//! @brief Get next initialized sheet
//!
//! If current sheet is the only sheet initialized, current sheet is returned.
//!
//! @param sheet Current sheet
//! @return next initialized sheet
//! @retval -1 no sheet is initialized
int8_t eeprom_next_initialized_sheet(int8_t sheet)
{
for (int8_t i = 0; i < static_cast<int8_t>(sizeof(Sheets::s)/sizeof(Sheet)); ++i)
{
++sheet;
if (sheet >= static_cast<int8_t>(sizeof(Sheets::s)/sizeof(Sheet))) sheet = 0;
if (eeprom_is_sheet_initialized(sheet)) return sheet;
}
return -1;
}
void eeprom_switch_to_next_sheet()
{
int8_t sheet = eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet));
sheet = eeprom_next_initialized_sheet(sheet);
if (sheet >= 0) eeprom_update_byte(&(EEPROM_Sheets_base->active_sheet), sheet);
}

248
Firmware/eeprom.h Normal file
View File

@ -0,0 +1,248 @@
#ifndef EEPROM_H
#define EEPROM_H
#include <stdint.h>
#define MAX_SHEETS 8
#define MAX_SHEET_NAME_LENGTH 7
typedef struct
{
char name[MAX_SHEET_NAME_LENGTH]; //!< Can be null terminated, doesn't need to be null terminated
int16_t z_offset; //!< Z_BABYSTEP_MIN .. Z_BABYSTEP_MAX = Z_BABYSTEP_MIN*2/1000 [mm] .. Z_BABYSTEP_MAX*2/1000 [mm]
uint8_t bed_temp; //!< 0 .. 254 [°C]
uint8_t pinda_temp; //!< 0 .. 254 [°C]
} Sheet;
typedef struct
{
Sheet s[MAX_SHEETS];
uint8_t active_sheet;
} Sheets;
// sizeof(Sheets). Do not change it unless EEPROM_Sheets_base is last item in EEPROM.
// Otherwise it would move following items.
#define EEPROM_SHEETS_SIZEOF 89
#ifdef __cplusplus
static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEPROM_SHEETS_SIZEOF.");
#endif
#define EEPROM_EMPTY_VALUE 0xFF
#define EEPROM_EMPTY_VALUE16 0xFFFF
// The total size of the EEPROM is
// 4096 for the Atmega2560
#define EEPROM_TOP 4096
#define EEPROM_SILENT 4095
#define EEPROM_LANG 4094
#define EEPROM_BABYSTEP_X 4092 //unused
#define EEPROM_BABYSTEP_Y 4090 //unused
#define EEPROM_BABYSTEP_Z 4088 //legacy, multiple values stored now in EEPROM_Sheets_base
#define EEPROM_CALIBRATION_STATUS 4087
#define EEPROM_BABYSTEP_Z0 4085
#define EEPROM_FILAMENTUSED 4081
// uint32_t
#define EEPROM_TOTALTIME 4077
#define EEPROM_BED_CALIBRATION_CENTER (EEPROM_TOTALTIME-2*4)
#define EEPROM_BED_CALIBRATION_VEC_X (EEPROM_BED_CALIBRATION_CENTER-2*4)
#define EEPROM_BED_CALIBRATION_VEC_Y (EEPROM_BED_CALIBRATION_VEC_X-2*4)
// Offsets of the Z heiths of the calibration points from the first point.
// The offsets are saved as 16bit signed int, scaled to tenths of microns.
#define EEPROM_BED_CALIBRATION_Z_JITTER (EEPROM_BED_CALIBRATION_VEC_Y-2*8)
#define EEPROM_FARM_MODE (EEPROM_BED_CALIBRATION_Z_JITTER-1)
#define EEPROM_FARM_NUMBER (EEPROM_FARM_MODE-3)
// Correction of the bed leveling, in micrometers.
// Maximum 50 micrometers allowed.
// Bed correction is valid if set to 1. If set to zero or 255, the successive 4 bytes are invalid.
#define EEPROM_BED_CORRECTION_VALID (EEPROM_FARM_NUMBER-1)
#define EEPROM_BED_CORRECTION_LEFT (EEPROM_BED_CORRECTION_VALID-1)
#define EEPROM_BED_CORRECTION_RIGHT (EEPROM_BED_CORRECTION_LEFT-1)
#define EEPROM_BED_CORRECTION_FRONT (EEPROM_BED_CORRECTION_RIGHT-1)
#define EEPROM_BED_CORRECTION_REAR (EEPROM_BED_CORRECTION_FRONT-1)
#define EEPROM_TOSHIBA_FLASH_AIR_COMPATIBLITY (EEPROM_BED_CORRECTION_REAR-1)
#define EEPROM_PRINT_FLAG (EEPROM_TOSHIBA_FLASH_AIR_COMPATIBLITY-1)
#define EEPROM_PROBE_TEMP_SHIFT (EEPROM_PRINT_FLAG - 2*5) //5 x int for storing pinda probe temp shift relative to 50 C; unit: motor steps
#define EEPROM_TEMP_CAL_ACTIVE (EEPROM_PROBE_TEMP_SHIFT - 1)
#define EEPROM_BOWDEN_LENGTH (EEPROM_TEMP_CAL_ACTIVE - 2*4) //4 x int for bowden lengths for multimaterial
#define EEPROM_CALIBRATION_STATUS_PINDA (EEPROM_BOWDEN_LENGTH - 1) //0 - not calibrated; 1 - calibrated
#define EEPROM_UVLO (EEPROM_CALIBRATION_STATUS_PINDA - 1) //1 - uvlo during print
#define EEPROM_UVLO_CURRENT_POSITION (EEPROM_UVLO-2*4) // 2 x float for current_position in X and Y axes
#define EEPROM_FILENAME (EEPROM_UVLO_CURRENT_POSITION - 8) //8chars to store filename without extension
#define EEPROM_FILE_POSITION (EEPROM_FILENAME - 4) //32 bit for uint32_t file position
#define EEPROM_UVLO_CURRENT_POSITION_Z (EEPROM_FILE_POSITION - 4) //float for current position in Z
#define EEPROM_UVLO_TARGET_HOTEND (EEPROM_UVLO_CURRENT_POSITION_Z - 1)
#define EEPROM_UVLO_TARGET_BED (EEPROM_UVLO_TARGET_HOTEND - 1)
#define EEPROM_UVLO_FEEDRATE (EEPROM_UVLO_TARGET_BED - 2)
#define EEPROM_UVLO_FAN_SPEED (EEPROM_UVLO_FEEDRATE - 1)
#define EEPROM_FAN_CHECK_ENABLED (EEPROM_UVLO_FAN_SPEED - 1)
#define EEPROM_UVLO_MESH_BED_LEVELING (EEPROM_FAN_CHECK_ENABLED - 9*2)
#define EEPROM_UVLO_Z_MICROSTEPS (EEPROM_UVLO_MESH_BED_LEVELING - 2)
#define EEPROM_UVLO_E_ABS (EEPROM_UVLO_Z_MICROSTEPS - 1)
#define EEPROM_UVLO_CURRENT_POSITION_E (EEPROM_UVLO_E_ABS - 4) //float for current position in E
// Crash detection mode EEPROM setting
#define EEPROM_CRASH_DET (EEPROM_UVLO_CURRENT_POSITION_E - 5) // float (orig EEPROM_UVLO_MESH_BED_LEVELING-12)
// Crash detection counter Y (last print)
#define EEPROM_CRASH_COUNT_Y (EEPROM_CRASH_DET - 1) // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-15)
// Filament sensor on/off EEPROM setting
#define EEPROM_FSENSOR (EEPROM_CRASH_COUNT_Y - 1) // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-14)
// Crash detection counter X (last print)
#define EEPROM_CRASH_COUNT_X (EEPROM_FSENSOR - 1) // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-15)
// Filament runout/error coutner (last print)
#define EEPROM_FERROR_COUNT (EEPROM_CRASH_COUNT_X - 1) // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-16)
// Power loss errors (last print)
#define EEPROM_POWER_COUNT (EEPROM_FERROR_COUNT - 1) // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-17)
#define EEPROM_XYZ_CAL_SKEW (EEPROM_POWER_COUNT - 4) // float for skew backup
#define EEPROM_WIZARD_ACTIVE (EEPROM_XYZ_CAL_SKEW - 1)
#define EEPROM_BELTSTATUS_X (EEPROM_WIZARD_ACTIVE - 2) // uint16
#define EEPROM_BELTSTATUS_Y (EEPROM_BELTSTATUS_X - 2) // uint16
#define EEPROM_DIR_DEPTH (EEPROM_BELTSTATUS_Y-1)
#define EEPROM_DIRS (EEPROM_DIR_DEPTH-80) //8 chars for each dir name, max 10 levels
#define EEPROM_SD_SORT (EEPROM_DIRS - 1) //0 -time, 1-alpha, 2-none
#define EEPROM_SECOND_SERIAL_ACTIVE (EEPROM_SD_SORT - 1)
#define EEPROM_FSENS_AUTOLOAD_ENABLED (EEPROM_SECOND_SERIAL_ACTIVE - 1)
// Crash detection counter X (total)
#define EEPROM_CRASH_COUNT_X_TOT (EEPROM_FSENS_AUTOLOAD_ENABLED - 2) // uint16
// Crash detection counter Y (total)
#define EEPROM_CRASH_COUNT_Y_TOT (EEPROM_CRASH_COUNT_X_TOT - 2) // uint16
// Filament runout/error coutner (total)
#define EEPROM_FERROR_COUNT_TOT (EEPROM_CRASH_COUNT_Y_TOT - 2) // uint16
// Power loss errors (total)
#define EEPROM_POWER_COUNT_TOT (EEPROM_FERROR_COUNT_TOT - 2) // uint16
////////////////////////////////////////
// TMC2130 Accurate sensorless homing
// X-axis home origin (stepper phase in microsteps, 0..63 for 16ustep resolution)
#define EEPROM_TMC2130_HOME_X_ORIGIN (EEPROM_POWER_COUNT_TOT - 1) // uint8
// X-axis home bsteps (number of microsteps backward)
#define EEPROM_TMC2130_HOME_X_BSTEPS (EEPROM_TMC2130_HOME_X_ORIGIN - 1) // uint8
// X-axis home fsteps (number of microsteps forward)
#define EEPROM_TMC2130_HOME_X_FSTEPS (EEPROM_TMC2130_HOME_X_BSTEPS - 1) // uint8
// Y-axis home origin (stepper phase in microsteps, 0..63 for 16ustep resolution)
#define EEPROM_TMC2130_HOME_Y_ORIGIN (EEPROM_TMC2130_HOME_X_FSTEPS - 1) // uint8
// X-axis home bsteps (number of microsteps backward)
#define EEPROM_TMC2130_HOME_Y_BSTEPS (EEPROM_TMC2130_HOME_Y_ORIGIN - 1) // uint8
// X-axis home fsteps (number of microsteps forward)
#define EEPROM_TMC2130_HOME_Y_FSTEPS (EEPROM_TMC2130_HOME_Y_BSTEPS - 1) // uint8
// Accurate homing enabled
#define EEPROM_TMC2130_HOME_ENABLED (EEPROM_TMC2130_HOME_Y_FSTEPS - 1) // uint8
////////////////////////////////////////
// TMC2130 uStep linearity correction
// Linearity correction factor (XYZE)
#define EEPROM_TMC2130_WAVE_X_FAC (EEPROM_TMC2130_HOME_ENABLED - 1) // uint8
#define EEPROM_TMC2130_WAVE_Y_FAC (EEPROM_TMC2130_WAVE_X_FAC - 1) // uint8
#define EEPROM_TMC2130_WAVE_Z_FAC (EEPROM_TMC2130_WAVE_Y_FAC - 1) // uint8
#define EEPROM_TMC2130_WAVE_E_FAC (EEPROM_TMC2130_WAVE_Z_FAC - 1) // uint8
////////////////////////////////////////
// TMC2130 uStep resolution
// microstep resolution (XYZE): usteps = (256 >> mres)
#define EEPROM_TMC2130_X_MRES (EEPROM_TMC2130_WAVE_E_FAC - 1) // uint8
#define EEPROM_TMC2130_Y_MRES (EEPROM_TMC2130_X_MRES - 1) // uint8
#define EEPROM_TMC2130_Z_MRES (EEPROM_TMC2130_Y_MRES - 1) // uint8
#define EEPROM_TMC2130_E_MRES (EEPROM_TMC2130_Z_MRES - 1) // uint8
// HW
#define EEPROM_PRINTER_TYPE (EEPROM_TMC2130_E_MRES - 2) // uint16
#define EEPROM_BOARD_TYPE (EEPROM_PRINTER_TYPE - 2) // uint16
// Extruder multiplier for power panic
#define EEPROM_EXTRUDER_MULTIPLIER_0 (EEPROM_BOARD_TYPE - 4) //float
#define EEPROM_EXTRUDER_MULTIPLIER_1 (EEPROM_EXTRUDER_MULTIPLIER_0 - 4) //float
#define EEPROM_EXTRUDER_MULTIPLIER_2 (EEPROM_EXTRUDER_MULTIPLIER_1 - 4) //float
#define EEPROM_EXTRUDEMULTIPLY (EEPROM_EXTRUDER_MULTIPLIER_2 - 2) // uint16
//
#define EEPROM_UVLO_TINY_CURRENT_POSITION_Z (EEPROM_EXTRUDEMULTIPLY-4) // float
#define EEPROM_UVLO_TINY_Z_MICROSTEPS (EEPROM_UVLO_TINY_CURRENT_POSITION_Z-2) // uint16
// Sound Mode
//#define EEPROM_SOUND_MODE (EEPROM_EXTRUDEMULTIPLY-1) // uint8
#define EEPROM_SOUND_MODE (EEPROM_UVLO_TINY_Z_MICROSTEPS-1) // uint8
#define EEPROM_AUTO_DEPLETE (EEPROM_SOUND_MODE-1) //bool
#define EEPROM_FSENS_OQ_MEASS_ENABLED (EEPROM_AUTO_DEPLETE - 1) //bool
#define EEPROM_MMU_FAIL_TOT (EEPROM_FSENS_OQ_MEASS_ENABLED - 2) //uint16_t
#define EEPROM_MMU_FAIL (EEPROM_MMU_FAIL_TOT - 1) //uint8_t
#define EEPROM_MMU_LOAD_FAIL_TOT (EEPROM_MMU_FAIL - 2) //uint16_t
#define EEPROM_MMU_LOAD_FAIL (EEPROM_MMU_LOAD_FAIL_TOT - 1) //uint8_t
#define EEPROM_MMU_CUTTER_ENABLED (EEPROM_MMU_LOAD_FAIL - 1)
#define EEPROM_UVLO_MESH_BED_LEVELING_FULL (EEPROM_MMU_CUTTER_ENABLED - 12*12*2) //allow 12 calibration points for future expansion
#define EEPROM_MBL_TYPE (EEPROM_UVLO_MESH_BED_LEVELING_FULL-1) //uint8_t for mesh bed leveling precision
#define EEPROM_MBL_MAGNET_ELIMINATION (EEPROM_MBL_TYPE -1)
#define EEPROM_MBL_POINTS_NR (EEPROM_MBL_MAGNET_ELIMINATION -1) //uint8_t number of points in one exis for mesh bed leveling
#define EEPROM_MBL_PROBE_NR (EEPROM_MBL_POINTS_NR-1) //number of measurements for each point
#define EEPROM_MMU_STEALTH (EEPROM_MBL_PROBE_NR-1)
#define EEPROM_CHECK_MODE (EEPROM_MMU_STEALTH-1) // uint8
#define EEPROM_NOZZLE_DIAMETER (EEPROM_CHECK_MODE-1) // uint8
#define EEPROM_NOZZLE_DIAMETER_uM (EEPROM_NOZZLE_DIAMETER-2) // uint16
#define EEPROM_CHECK_MODEL (EEPROM_NOZZLE_DIAMETER_uM-1) // uint8
#define EEPROM_CHECK_VERSION (EEPROM_CHECK_MODEL-1) // uint8
#define EEPROM_CHECK_GCODE (EEPROM_CHECK_VERSION-1) // uint8
#define EEPROM_SHEETS_BASE (EEPROM_CHECK_GCODE - EEPROM_SHEETS_SIZEOF) // Sheets
static Sheets * const EEPROM_Sheets_base = (Sheets*)(EEPROM_SHEETS_BASE);
//This is supposed to point to last item to allow EEPROM overrun check. Please update when adding new items.
#define EEPROM_LAST_ITEM EEPROM_SHEETS_BASE
// !!!!!
// !!!!! this is end of EEPROM section ... all updates MUST BE inserted before this mark !!!!!
// !!!!!
// Currently running firmware, each digit stored as uint16_t.
// The flavor differentiates a dev, alpha, beta, release candidate or a release version.
#define EEPROM_FIRMWARE_VERSION_END (FW_PRUSA3D_MAGIC_LEN+8)
#define EEPROM_FIRMWARE_VERSION_FLAVOR (FW_PRUSA3D_MAGIC_LEN+6)
#define EEPROM_FIRMWARE_VERSION_REVISION (FW_PRUSA3D_MAGIC_LEN+4)
#define EEPROM_FIRMWARE_VERSION_MINOR (FW_PRUSA3D_MAGIC_LEN+2)
#define EEPROM_FIRMWARE_VERSION_MAJOR FW_PRUSA3D_MAGIC_LEN
// Magic string, indicating that the current or the previous firmware running was the Prusa3D firmware.
#define EEPROM_FIRMWARE_PRUSA_MAGIC 0
#ifdef __cplusplus
#include "ConfigurationStore.h"
static_assert(EEPROM_FIRMWARE_VERSION_END < 20, "Firmware version EEPROM address conflicts with EEPROM_M500_base");
static constexpr M500_conf * const EEPROM_M500_base = reinterpret_cast<M500_conf*>(20); //offset for storing settings using M500
static_assert(((sizeof(M500_conf) + 20) < EEPROM_LAST_ITEM), "M500_conf address space conflicts with previous items.");
#endif
enum
{
EEPROM_MMU_CUTTER_ENABLED_enabled = 1,
EEPROM_MMU_CUTTER_ENABLED_always = 2,
};
#ifdef __cplusplus
void eeprom_init();
bool eeprom_is_sheet_initialized(uint8_t sheet_num);
struct SheetName
{
char c[sizeof(Sheet::name) + 1];
};
void eeprom_default_sheet_name(uint8_t index, SheetName &sheetName);
int8_t eeprom_next_initialized_sheet(int8_t sheet);
void eeprom_switch_to_next_sheet();
#endif
#endif // EEPROM_H

4058
Firmware/fastio.h Normal file

File diff suppressed because it is too large Load Diff

213
Firmware/first_lay_cal.cpp Normal file
View File

@ -0,0 +1,213 @@
//! @file
//! @date Jun 10, 2019
//! @author Marek Bel
//! @brief First layer (Z offset) calibration
#include "first_lay_cal.h"
#include "Configuration_prusa.h"
#include "language.h"
#include "Marlin.h"
#include "mmu.h"
#include <avr/pgmspace.h>
//! @brief Wait for preheat
void lay1cal_wait_preheat()
{
static const char cmd_preheat_0[] PROGMEM = "M107";
static const char cmd_preheat_1[] PROGMEM = "M190";
static const char cmd_preheat_2[] PROGMEM = "M109";
static const char cmd_preheat_4[] PROGMEM = "G28";
static const char cmd_preheat_5[] PROGMEM = "G92 E0.0";
const char * const preheat_cmd[] =
{
cmd_preheat_0,
cmd_preheat_1,
cmd_preheat_2,
_T(MSG_M117_V2_CALIBRATION),
cmd_preheat_4,
cmd_preheat_5,
};
for (uint8_t i = 0; i < (sizeof(preheat_cmd)/sizeof(preheat_cmd[0])); ++i)
{
enquecommand_P(preheat_cmd[i]);
}
}
//! @brief Load filament
//! @param cmd_buffer character buffer needed to format gcodes
//! @param filament filament to use (applies for MMU only)
void lay1cal_load_filament(char *cmd_buffer, uint8_t filament)
{
if (mmu_enabled)
{
enquecommand_P(PSTR("M83"));
enquecommand_P(PSTR("G1 Y-3.0 F1000.0"));
enquecommand_P(PSTR("G1 Z0.4 F1000.0"));
sprintf_P(cmd_buffer, PSTR("T%d"), filament);
enquecommand(cmd_buffer);
}
}
//! @brief Print intro line
void lay1cal_intro_line()
{
static const char cmd_intro_mmu_3[] PROGMEM = "G1 X55.0 E32.0 F1073.0";
static const char cmd_intro_mmu_4[] PROGMEM = "G1 X5.0 E32.0 F1800.0";
static const char cmd_intro_mmu_5[] PROGMEM = "G1 X55.0 E8.0 F2000.0";
static const char cmd_intro_mmu_6[] PROGMEM = "G1 Z0.3 F1000.0";
static const char cmd_intro_mmu_7[] PROGMEM = "G92 E0.0";
static const char cmd_intro_mmu_8[] PROGMEM = "G1 X240.0 E25.0 F2200.0";
static const char cmd_intro_mmu_9[] PROGMEM = "G1 Y-2.0 F1000.0";
static const char cmd_intro_mmu_10[] PROGMEM = "G1 X55.0 E25 F1400.0";
static const char cmd_intro_mmu_11[] PROGMEM = "G1 Z0.20 F1000.0";
static const char cmd_intro_mmu_12[] PROGMEM = "G1 X5.0 E4.0 F1000.0";
static const char * const intro_mmu_cmd[] PROGMEM =
{
cmd_intro_mmu_3,
cmd_intro_mmu_4,
cmd_intro_mmu_5,
cmd_intro_mmu_6,
cmd_intro_mmu_7,
cmd_intro_mmu_8,
cmd_intro_mmu_9,
cmd_intro_mmu_10,
cmd_intro_mmu_11,
cmd_intro_mmu_12,
};
if (mmu_enabled)
{
for (uint8_t i = 0; i < (sizeof(intro_mmu_cmd)/sizeof(intro_mmu_cmd[0])); ++i)
{
enquecommand_P(static_cast<char*>(pgm_read_ptr(&intro_mmu_cmd[i])));
}
}
else
{
enquecommand_P(PSTR("G1 X60.0 E9.0 F1000.0"));
enquecommand_P(PSTR("G1 X100.0 E12.5 F1000.0"));
}
}
//! @brief Setup for printing meander
void lay1cal_before_meander()
{
static const char cmd_pre_meander_0[] PROGMEM = "G92 E0.0";
static const char cmd_pre_meander_1[] PROGMEM = "G21"; //set units to millimeters TODO unsupported command
static const char cmd_pre_meander_2[] PROGMEM = "G90"; //use absolute coordinates
static const char cmd_pre_meander_3[] PROGMEM = "M83"; //use relative distances for extrusion TODO: duplicate
static const char cmd_pre_meander_4[] PROGMEM = "G1 E-1.50000 F2100.00000";
static const char cmd_pre_meander_5[] PROGMEM = "G1 Z5 F7200.000";
static const char cmd_pre_meander_6[] PROGMEM = "M204 S1000"; //set acceleration
static const char cmd_pre_meander_7[] PROGMEM = "G1 F4000";
static const char * const cmd_pre_meander[] PROGMEM =
{
cmd_pre_meander_0,
cmd_pre_meander_1,
cmd_pre_meander_2,
cmd_pre_meander_3,
cmd_pre_meander_4,
cmd_pre_meander_5,
cmd_pre_meander_6,
cmd_pre_meander_7,
};
for (uint8_t i = 0; i < (sizeof(cmd_pre_meander)/sizeof(cmd_pre_meander[0])); ++i)
{
enquecommand_P(static_cast<char*>(pgm_read_ptr(&cmd_pre_meander[i])));
}
}
//! @brief Count extrude length
//!
//! @param layer_height layer height in mm
//! @param extrusion_width extrusion width in mm
//! @param extrusion_length extrusion length in mm
//! @return filament length in mm which needs to be extruded to form line
static constexpr float count_e(float layer_height, float extrusion_width, float extrusion_length)
{
return (extrusion_length * layer_height * extrusion_width / (M_PI * pow(1.75, 2) / 4));
}
static const float width = 0.4; //!< line width
static const float length = 20 - width; //!< line length
static const float height = 0.2; //!< layer height TODO This is wrong, as current Z height is 0.15 mm
static const float extr = count_e(height, width, length); //!< E axis movement needed to print line
//! @brief Print meander
//! @param cmd_buffer character buffer needed to format gcodes
void lay1cal_meander(char *cmd_buffer)
{
static const char cmd_meander_0[] PROGMEM = "G1 X50 Y155";
static const char cmd_meander_1[] PROGMEM = "G1 Z0.150 F7200.000";
static const char cmd_meander_2[] PROGMEM = "G1 F1080";
static const char cmd_meander_3[] PROGMEM = "G1 X75 Y155 E2.5";
static const char cmd_meander_4[] PROGMEM = "G1 X100 Y155 E2";
static const char cmd_meander_5[] PROGMEM = "G1 X200 Y155 E2.62773";
static const char cmd_meander_6[] PROGMEM = "G1 X200 Y135 E0.66174";
static const char cmd_meander_7[] PROGMEM = "G1 X50 Y135 E3.62773";
static const char cmd_meander_8[] PROGMEM = "G1 X50 Y115 E0.49386";
static const char cmd_meander_9[] PROGMEM = "G1 X200 Y115 E3.62773";
static const char cmd_meander_10[] PROGMEM = "G1 X200 Y95 E0.49386";
static const char cmd_meander_11[] PROGMEM = "G1 X50 Y95 E3.62773";
static const char cmd_meander_12[] PROGMEM = "G1 X50 Y75 E0.49386";
static const char cmd_meander_13[] PROGMEM = "G1 X200 Y75 E3.62773";
static const char cmd_meander_14[] PROGMEM = "G1 X200 Y55 E0.49386";
static const char cmd_meander_15[] PROGMEM = "G1 X50 Y55 E3.62773";
static const char * const cmd_meander[] PROGMEM =
{
cmd_meander_0,
cmd_meander_1,
cmd_meander_2,
cmd_meander_3,
cmd_meander_4,
cmd_meander_5,
cmd_meander_6,
cmd_meander_7,
cmd_meander_8,
cmd_meander_9,
cmd_meander_10,
cmd_meander_11,
cmd_meander_12,
cmd_meander_13,
cmd_meander_14,
cmd_meander_15,
};
for (uint8_t i = 0; i < (sizeof(cmd_meander)/sizeof(cmd_meander[0])); ++i)
{
enquecommand_P(static_cast<char*>(pgm_read_ptr(&cmd_meander[i])));
}
sprintf_P(cmd_buffer, PSTR("G1 X50 Y35 E%-.3f"), extr);
enquecommand(cmd_buffer);
}
//! @brief Print square
//!
//! This function needs to be called 16 times for i from 0 to 15.
//!
//! @param cmd_buffer character buffer needed to format gcodes
//! @param i iteration
void lay1cal_square(char *cmd_buffer, uint8_t i)
{
const float extr_short_segment = count_e(height, width, width);
static const char fmt1[] PROGMEM = "G1 X%d Y%-.2f E%-.3f";
static const char fmt2[] PROGMEM = "G1 Y%-.2f E%-.3f";
sprintf_P(cmd_buffer, fmt1, 70, (35 - i*width * 2), extr);
enquecommand(cmd_buffer);
sprintf_P(cmd_buffer, fmt2, (35 - (2 * i + 1)*width), extr_short_segment);
enquecommand(cmd_buffer);
sprintf_P(cmd_buffer, fmt1, 50, (35 - (2 * i + 1)*width), extr);
enquecommand(cmd_buffer);
sprintf_P(cmd_buffer, fmt2, (35 - (i + 1)*width * 2), extr_short_segment);
enquecommand(cmd_buffer);
}

16
Firmware/first_lay_cal.h Normal file
View File

@ -0,0 +1,16 @@
//! @file
//! @date Jun 10, 2019
//! @author Marek Bel
#ifndef FIRMWARE_FIRST_LAY_CAL_H_
#define FIRMWARE_FIRST_LAY_CAL_H_
#include <stdint.h>
void lay1cal_wait_preheat();
void lay1cal_load_filament(char *cmd_buffer, uint8_t filament);
void lay1cal_intro_line();
void lay1cal_before_meander();
void lay1cal_meander(char *cmd_buffer);
void lay1cal_square(char *cmd_buffer, uint8_t i);
#endif /* FIRMWARE_FIRST_LAY_CAL_H_ */

598
Firmware/fsensor.cpp Normal file
View File

@ -0,0 +1,598 @@
//! @file
#include "Marlin.h"
#include "fsensor.h"
#include <avr/pgmspace.h>
#include "pat9125.h"
#include "stepper.h"
#include "planner.h"
#include "fastio.h"
#include "io_atmega2560.h"
#include "cmdqueue.h"
#include "ultralcd.h"
#include "ConfigurationStore.h"
#include "mmu.h"
#include "cardreader.h"
//! @name Basic parameters
//! @{
#define FSENSOR_CHUNK_LEN 0.64F //!< filament sensor chunk length 0.64mm
#define FSENSOR_ERR_MAX 17 //!< filament sensor maximum error count for runout detection
//! @}
//! @name Optical quality measurement parameters
//! @{
#define FSENSOR_OQ_MAX_ES 6 //!< maximum error sum while loading (length ~64mm = 100chunks)
#define FSENSOR_OQ_MAX_EM 2 //!< maximum error counter value while loading
#define FSENSOR_OQ_MIN_YD 2 //!< minimum yd per chunk (applied to avg value)
#define FSENSOR_OQ_MAX_YD 200 //!< maximum yd per chunk (applied to avg value)
#define FSENSOR_OQ_MAX_PD 4 //!< maximum positive deviation (= yd_max/yd_avg)
#define FSENSOR_OQ_MAX_ND 5 //!< maximum negative deviation (= yd_avg/yd_min)
#define FSENSOR_OQ_MAX_SH 13 //!< maximum shutter value
//! @}
const char ERRMSG_PAT9125_NOT_RESP[] PROGMEM = "PAT9125 not responding (%d)!\n";
// PJ7 can not be used (does not have PinChangeInterrupt possibility)
#define FSENSOR_INT_PIN 75 //!< filament sensor interrupt pin PJ4
#define FSENSOR_INT_PIN_MASK 0x10 //!< filament sensor interrupt pin mask (bit4)
#define FSENSOR_INT_PIN_PIN_REG PINJ // PIN register @ PJ4
#define FSENSOR_INT_PIN_VECT PCINT1_vect // PinChange ISR @ PJ4
#define FSENSOR_INT_PIN_PCMSK_REG PCMSK1 // PinChangeMaskRegister @ PJ4
#define FSENSOR_INT_PIN_PCMSK_BIT PCINT13 // PinChange Interrupt / PinChange Enable Mask @ PJ4
#define FSENSOR_INT_PIN_PCICR_BIT PCIE1 // PinChange Interrupt Enable / Flag @ PJ4
//uint8_t fsensor_int_pin = FSENSOR_INT_PIN;
uint8_t fsensor_int_pin_old = 0;
int16_t fsensor_chunk_len = 0;
//! enabled = initialized and sampled every chunk event
bool fsensor_enabled = true;
//! runout watching is done in fsensor_update (called from main loop)
bool fsensor_watch_runout = true;
//! not responding - is set if any communication error occurred during initialization or readout
bool fsensor_not_responding = false;
//! printing saved
bool fsensor_printing_saved = false;
//! enable/disable quality meassurement
bool fsensor_oq_meassure_enabled = false;
//! as explained in the CHECK_FSENSOR macro: this flag is set to true when fsensor posts
//! the M600 into the command queue, which elliminates the hazard of having posted multiple M600's
//! before the first one gets read and started processing.
//! Btw., the IR fsensor could do up to 6 posts before the command queue managed to start processing the first M600 ;)
static bool fsensor_m600_enqueued = false;
//! number of errors, updated in ISR
uint8_t fsensor_err_cnt = 0;
//! variable for accumulating step count (updated callbacks from stepper and ISR)
int16_t fsensor_st_cnt = 0;
//! last dy value from pat9125 sensor (used in ISR)
int16_t fsensor_dy_old = 0;
//! log flag: 0=log disabled, 1=log enabled
uint8_t fsensor_log = 1;
//! @name filament autoload variables
//! @{
//! autoload feature enabled
bool fsensor_autoload_enabled = true;
//! autoload watching enable/disable flag
bool fsensor_watch_autoload = false;
//
uint16_t fsensor_autoload_y;
//
uint8_t fsensor_autoload_c;
//
uint32_t fsensor_autoload_last_millis;
//
uint8_t fsensor_autoload_sum;
//! @}
//! @name filament optical quality measurement variables
//! @{
//! Measurement enable/disable flag
bool fsensor_oq_meassure = false;
//! skip-chunk counter, for accurate measurement is necessary to skip first chunk...
uint8_t fsensor_oq_skipchunk;
//! number of samples from start of measurement
uint8_t fsensor_oq_samples;
//! sum of steps in positive direction movements
uint16_t fsensor_oq_st_sum;
//! sum of deltas in positive direction movements
uint16_t fsensor_oq_yd_sum;
//! sum of errors during measurement
uint16_t fsensor_oq_er_sum;
//! max error counter value during measurement
uint8_t fsensor_oq_er_max;
//! minimum delta value
int16_t fsensor_oq_yd_min;
//! maximum delta value
int16_t fsensor_oq_yd_max;
//! sum of shutter value
uint16_t fsensor_oq_sh_sum;
//! @}
void fsensor_stop_and_save_print(void)
{
printf_P(PSTR("fsensor_stop_and_save_print\n"));
stop_and_save_print_to_ram(0, 0); //XYZE - no change
}
void fsensor_restore_print_and_continue(void)
{
printf_P(PSTR("fsensor_restore_print_and_continue\n"));
fsensor_watch_runout = true;
fsensor_err_cnt = 0;
fsensor_m600_enqueued = false;
restore_print_from_ram_and_continue(0); //XYZ = orig, E - no change
}
void fsensor_init(void)
{
#ifdef PAT9125
uint8_t pat9125 = pat9125_init();
printf_P(PSTR("PAT9125_init:%hhu\n"), pat9125);
#endif //PAT9125
uint8_t fsensor = eeprom_read_byte((uint8_t*)EEPROM_FSENSOR);
fsensor_autoload_enabled=eeprom_read_byte((uint8_t*)EEPROM_FSENS_AUTOLOAD_ENABLED);
#ifdef PAT9125
uint8_t oq_meassure_enabled = eeprom_read_byte((uint8_t*)EEPROM_FSENS_OQ_MEASS_ENABLED);
fsensor_oq_meassure_enabled = (oq_meassure_enabled == 1)?true:false;
fsensor_chunk_len = (int16_t)(FSENSOR_CHUNK_LEN * cs.axis_steps_per_unit[E_AXIS]);
if (!pat9125)
{
fsensor = 0; //disable sensor
fsensor_not_responding = true;
}
else
fsensor_not_responding = false;
#endif //PAT9125
if (fsensor)
fsensor_enable();
else
fsensor_disable();
printf_P(PSTR("FSensor %S\n"), (fsensor_enabled?PSTR("ENABLED"):PSTR("DISABLED\n")));
if (check_for_ir_sensor()) ir_sensor_detected = true;
}
bool fsensor_enable(void)
{
#ifdef PAT9125
if (mmu_enabled == false) { //filament sensor is pat9125, enable only if it is working
uint8_t pat9125 = pat9125_init();
printf_P(PSTR("PAT9125_init:%hhu\n"), pat9125);
if (pat9125)
fsensor_not_responding = false;
else
fsensor_not_responding = true;
fsensor_enabled = pat9125 ? true : false;
fsensor_watch_runout = true;
fsensor_oq_meassure = false;
fsensor_err_cnt = 0;
fsensor_dy_old = 0;
eeprom_update_byte((uint8_t*)EEPROM_FSENSOR, fsensor_enabled ? 0x01 : 0x00);
FSensorStateMenu = fsensor_enabled ? 1 : 0;
}
else //filament sensor is FINDA, always enable
{
fsensor_enabled = true;
eeprom_update_byte((uint8_t*)EEPROM_FSENSOR, 0x01);
FSensorStateMenu = 1;
}
#else // PAT9125
fsensor_enabled = true;
eeprom_update_byte((uint8_t*)EEPROM_FSENSOR, 0x01);
FSensorStateMenu = 1;
#endif // PAT9125
return fsensor_enabled;
}
void fsensor_disable(void)
{
fsensor_enabled = false;
eeprom_update_byte((uint8_t*)EEPROM_FSENSOR, 0x00);
FSensorStateMenu = 0;
}
void fsensor_autoload_set(bool State)
{
#ifdef PAT9125
if (!State) fsensor_autoload_check_stop();
#endif //PAT9125
fsensor_autoload_enabled = State;
eeprom_update_byte((unsigned char *)EEPROM_FSENS_AUTOLOAD_ENABLED, fsensor_autoload_enabled);
}
void pciSetup(byte pin)
{
// !!! "digitalPinTo?????bit()" does not provide the correct results for some MCU pins
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin
PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
#ifdef PAT9125
void fsensor_autoload_check_start(void)
{
// puts_P(_N("fsensor_autoload_check_start\n"));
if (!fsensor_enabled) return;
if (!fsensor_autoload_enabled) return;
if (fsensor_watch_autoload) return;
if (!pat9125_update_y()) //update sensor
{
fsensor_disable();
fsensor_not_responding = true;
fsensor_watch_autoload = false;
printf_P(ERRMSG_PAT9125_NOT_RESP, 3);
return;
}
puts_P(_N("fsensor_autoload_check_start - autoload ENABLED\n"));
fsensor_autoload_y = pat9125_y; //save current y value
fsensor_autoload_c = 0; //reset number of changes counter
fsensor_autoload_sum = 0;
fsensor_autoload_last_millis = _millis();
fsensor_watch_runout = false;
fsensor_watch_autoload = true;
fsensor_err_cnt = 0;
}
void fsensor_autoload_check_stop(void)
{
// puts_P(_N("fsensor_autoload_check_stop\n"));
if (!fsensor_enabled) return;
// puts_P(_N("fsensor_autoload_check_stop 1\n"));
if (!fsensor_autoload_enabled) return;
// puts_P(_N("fsensor_autoload_check_stop 2\n"));
if (!fsensor_watch_autoload) return;
puts_P(_N("fsensor_autoload_check_stop - autoload DISABLED\n"));
fsensor_autoload_sum = 0;
fsensor_watch_autoload = false;
fsensor_watch_runout = true;
fsensor_err_cnt = 0;
}
#endif //PAT9125
bool fsensor_check_autoload(void)
{
if (!fsensor_enabled) return false;
if (!fsensor_autoload_enabled) return false;
if (ir_sensor_detected) {
if (digitalRead(IR_SENSOR_PIN) == 1) {
fsensor_watch_autoload = true;
}
else if (fsensor_watch_autoload == true) {
fsensor_watch_autoload = false;
return true;
}
}
#ifdef PAT9125
if (!fsensor_watch_autoload)
{
fsensor_autoload_check_start();
return false;
}
#if 0
uint8_t fsensor_autoload_c_old = fsensor_autoload_c;
#endif
if ((_millis() - fsensor_autoload_last_millis) < 25) return false;
fsensor_autoload_last_millis = _millis();
if (!pat9125_update_y()) //update sensor
{
fsensor_disable();
fsensor_not_responding = true;
printf_P(ERRMSG_PAT9125_NOT_RESP, 2);
return false;
}
int16_t dy = pat9125_y - fsensor_autoload_y;
if (dy) //? dy value is nonzero
{
if (dy > 0) //? delta-y value is positive (inserting)
{
fsensor_autoload_sum += dy;
fsensor_autoload_c += 3; //increment change counter by 3
}
else if (fsensor_autoload_c > 1)
fsensor_autoload_c -= 2; //decrement change counter by 2
fsensor_autoload_y = pat9125_y; //save current value
}
else if (fsensor_autoload_c > 0)
fsensor_autoload_c--;
if (fsensor_autoload_c == 0) fsensor_autoload_sum = 0;
#if 0
puts_P(_N("fsensor_check_autoload\n"));
if (fsensor_autoload_c != fsensor_autoload_c_old)
printf_P(PSTR("fsensor_check_autoload dy=%d c=%d sum=%d\n"), dy, fsensor_autoload_c, fsensor_autoload_sum);
#endif
// if ((fsensor_autoload_c >= 15) && (fsensor_autoload_sum > 30))
if ((fsensor_autoload_c >= 12) && (fsensor_autoload_sum > 20))
{
// puts_P(_N("fsensor_check_autoload = true !!!\n"));
return true;
}
#endif //PAT9125
return false;
}
void fsensor_oq_meassure_set(bool State)
{
fsensor_oq_meassure_enabled = State;
eeprom_update_byte((unsigned char *)EEPROM_FSENS_OQ_MEASS_ENABLED, fsensor_oq_meassure_enabled);
}
void fsensor_oq_meassure_start(uint8_t skip)
{
if (!fsensor_enabled) return;
if (!fsensor_oq_meassure_enabled) return;
printf_P(PSTR("fsensor_oq_meassure_start\n"));
fsensor_oq_skipchunk = skip;
fsensor_oq_samples = 0;
fsensor_oq_st_sum = 0;
fsensor_oq_yd_sum = 0;
fsensor_oq_er_sum = 0;
fsensor_oq_er_max = 0;
fsensor_oq_yd_min = FSENSOR_OQ_MAX_YD;
fsensor_oq_yd_max = 0;
fsensor_oq_sh_sum = 0;
pat9125_update();
pat9125_y = 0;
fsensor_watch_runout = false;
fsensor_oq_meassure = true;
}
void fsensor_oq_meassure_stop(void)
{
if (!fsensor_enabled) return;
if (!fsensor_oq_meassure_enabled) return;
printf_P(PSTR("fsensor_oq_meassure_stop, %hhu samples\n"), fsensor_oq_samples);
printf_P(_N(" st_sum=%u yd_sum=%u er_sum=%u er_max=%hhu\n"), fsensor_oq_st_sum, fsensor_oq_yd_sum, fsensor_oq_er_sum, fsensor_oq_er_max);
printf_P(_N(" yd_min=%u yd_max=%u yd_avg=%u sh_avg=%u\n"), fsensor_oq_yd_min, fsensor_oq_yd_max, (uint16_t)((uint32_t)fsensor_oq_yd_sum * fsensor_chunk_len / fsensor_oq_st_sum), (uint16_t)(fsensor_oq_sh_sum / fsensor_oq_samples));
fsensor_oq_meassure = false;
fsensor_watch_runout = true;
fsensor_err_cnt = 0;
}
const char _OK[] PROGMEM = "OK";
const char _NG[] PROGMEM = "NG!";
bool fsensor_oq_result(void)
{
if (!fsensor_enabled) return true;
if (!fsensor_oq_meassure_enabled) return true;
printf_P(_N("fsensor_oq_result\n"));
bool res_er_sum = (fsensor_oq_er_sum <= FSENSOR_OQ_MAX_ES);
printf_P(_N(" er_sum = %u %S\n"), fsensor_oq_er_sum, (res_er_sum?_OK:_NG));
bool res_er_max = (fsensor_oq_er_max <= FSENSOR_OQ_MAX_EM);
printf_P(_N(" er_max = %hhu %S\n"), fsensor_oq_er_max, (res_er_max?_OK:_NG));
uint8_t yd_avg = ((uint32_t)fsensor_oq_yd_sum * fsensor_chunk_len / fsensor_oq_st_sum);
bool res_yd_avg = (yd_avg >= FSENSOR_OQ_MIN_YD) && (yd_avg <= FSENSOR_OQ_MAX_YD);
printf_P(_N(" yd_avg = %hhu %S\n"), yd_avg, (res_yd_avg?_OK:_NG));
bool res_yd_max = (fsensor_oq_yd_max <= (yd_avg * FSENSOR_OQ_MAX_PD));
printf_P(_N(" yd_max = %u %S\n"), fsensor_oq_yd_max, (res_yd_max?_OK:_NG));
bool res_yd_min = (fsensor_oq_yd_min >= (yd_avg / FSENSOR_OQ_MAX_ND));
printf_P(_N(" yd_min = %u %S\n"), fsensor_oq_yd_min, (res_yd_min?_OK:_NG));
uint16_t yd_dev = (fsensor_oq_yd_max - yd_avg) + (yd_avg - fsensor_oq_yd_min);
printf_P(_N(" yd_dev = %u\n"), yd_dev);
uint16_t yd_qua = 10 * yd_avg / (yd_dev + 1);
printf_P(_N(" yd_qua = %u %S\n"), yd_qua, ((yd_qua >= 8)?_OK:_NG));
uint8_t sh_avg = (fsensor_oq_sh_sum / fsensor_oq_samples);
bool res_sh_avg = (sh_avg <= FSENSOR_OQ_MAX_SH);
if (yd_qua >= 8) res_sh_avg = true;
printf_P(_N(" sh_avg = %hhu %S\n"), sh_avg, (res_sh_avg?_OK:_NG));
bool res = res_er_sum && res_er_max && res_yd_avg && res_yd_max && res_yd_min && res_sh_avg;
printf_P(_N("fsensor_oq_result %S\n"), (res?_OK:_NG));
return res;
}
#ifdef PAT9125
ISR(FSENSOR_INT_PIN_VECT)
{
if (mmu_enabled || ir_sensor_detected) return;
if (!((fsensor_int_pin_old ^ FSENSOR_INT_PIN_PIN_REG) & FSENSOR_INT_PIN_MASK)) return;
fsensor_int_pin_old = FSENSOR_INT_PIN_PIN_REG;
static bool _lock = false;
if (_lock) return;
_lock = true;
int st_cnt = fsensor_st_cnt;
fsensor_st_cnt = 0;
sei();
uint8_t old_err_cnt = fsensor_err_cnt;
uint8_t pat9125_res = fsensor_oq_meassure?pat9125_update():pat9125_update_y();
if (!pat9125_res)
{
fsensor_disable();
fsensor_not_responding = true;
printf_P(ERRMSG_PAT9125_NOT_RESP, 1);
}
if (st_cnt != 0)
{ //movement
if (st_cnt > 0) //positive movement
{
if (pat9125_y < 0)
{
if (fsensor_err_cnt)
fsensor_err_cnt += 2;
else
fsensor_err_cnt++;
}
else if (pat9125_y > 0)
{
if (fsensor_err_cnt)
fsensor_err_cnt--;
}
else //(pat9125_y == 0)
if (((fsensor_dy_old <= 0) || (fsensor_err_cnt)) && (st_cnt > (fsensor_chunk_len >> 1)))
fsensor_err_cnt++;
if (fsensor_oq_meassure)
{
if (fsensor_oq_skipchunk)
{
fsensor_oq_skipchunk--;
fsensor_err_cnt = 0;
}
else
{
if (st_cnt == fsensor_chunk_len)
{
if (pat9125_y > 0) if (fsensor_oq_yd_min > pat9125_y) fsensor_oq_yd_min = (fsensor_oq_yd_min + pat9125_y) / 2;
if (pat9125_y >= 0) if (fsensor_oq_yd_max < pat9125_y) fsensor_oq_yd_max = (fsensor_oq_yd_max + pat9125_y) / 2;
}
fsensor_oq_samples++;
fsensor_oq_st_sum += st_cnt;
if (pat9125_y > 0) fsensor_oq_yd_sum += pat9125_y;
if (fsensor_err_cnt > old_err_cnt)
fsensor_oq_er_sum += (fsensor_err_cnt - old_err_cnt);
if (fsensor_oq_er_max < fsensor_err_cnt)
fsensor_oq_er_max = fsensor_err_cnt;
fsensor_oq_sh_sum += pat9125_s;
}
}
}
else //negative movement
{
}
}
else
{ //no movement
}
#ifdef DEBUG_FSENSOR_LOG
if (fsensor_log)
{
printf_P(_N("FSENSOR cnt=%d dy=%d err=%hhu %S\n"), st_cnt, pat9125_y, fsensor_err_cnt, (fsensor_err_cnt > old_err_cnt)?_N("NG!"):_N("OK"));
if (fsensor_oq_meassure) printf_P(_N("FSENSOR st_sum=%u yd_sum=%u er_sum=%u er_max=%hhu yd_max=%u\n"), fsensor_oq_st_sum, fsensor_oq_yd_sum, fsensor_oq_er_sum, fsensor_oq_er_max, fsensor_oq_yd_max);
}
#endif //DEBUG_FSENSOR_LOG
fsensor_dy_old = pat9125_y;
pat9125_y = 0;
_lock = false;
return;
}
void fsensor_setup_interrupt(void)
{
pinMode(FSENSOR_INT_PIN, OUTPUT);
digitalWrite(FSENSOR_INT_PIN, LOW);
fsensor_int_pin_old = 0;
//pciSetup(FSENSOR_INT_PIN);
// !!! "pciSetup()" does not provide the correct results for some MCU pins
// so interrupt registers settings:
FSENSOR_INT_PIN_PCMSK_REG |= bit(FSENSOR_INT_PIN_PCMSK_BIT); // enable corresponding PinChangeInterrupt (individual pin)
PCIFR |= bit(FSENSOR_INT_PIN_PCICR_BIT); // clear previous occasional interrupt (set of pins)
PCICR |= bit(FSENSOR_INT_PIN_PCICR_BIT); // enable corresponding PinChangeInterrupt (set of pins)
}
#endif //PAT9125
void fsensor_st_block_begin(block_t* bl)
{
if (!fsensor_enabled) return;
if (((fsensor_st_cnt > 0) && (bl->direction_bits & 0x8)) ||
((fsensor_st_cnt < 0) && !(bl->direction_bits & 0x8)))
{
// !!! bit toggling (PINxn <- 1) (for PinChangeInterrupt) does not work for some MCU pins
if (PIN_GET(FSENSOR_INT_PIN)) {PIN_VAL(FSENSOR_INT_PIN, LOW);}
else {PIN_VAL(FSENSOR_INT_PIN, HIGH);}
}
}
void fsensor_st_block_chunk(block_t* bl, int cnt)
{
if (!fsensor_enabled) return;
fsensor_st_cnt += (bl->direction_bits & 0x8)?-cnt:cnt;
if ((fsensor_st_cnt >= fsensor_chunk_len) || (fsensor_st_cnt <= -fsensor_chunk_len))
{
// !!! bit toggling (PINxn <- 1) (for PinChangeInterrupt) does not work for some MCU pins
if (PIN_GET(FSENSOR_INT_PIN)) {PIN_VAL(FSENSOR_INT_PIN, LOW);}
else {PIN_VAL(FSENSOR_INT_PIN, HIGH);}
}
}
//! Common code for enqueing M600 and supplemental codes into the command queue.
//! Used both for the IR sensor and the PAT9125
void fsensor_enque_M600(){
printf_P(PSTR("fsensor_update - M600\n"));
eeprom_update_byte((uint8_t*)EEPROM_FERROR_COUNT, eeprom_read_byte((uint8_t*)EEPROM_FERROR_COUNT) + 1);
eeprom_update_word((uint16_t*)EEPROM_FERROR_COUNT_TOT, eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT) + 1);
enquecommand_front_P(PSTR("PRUSA fsensor_recover"));
fsensor_m600_enqueued = true;
enquecommand_front_P((PSTR("M600")));
}
//! @brief filament sensor update (perform M600 on filament runout)
//!
//! Works only if filament sensor is enabled.
//! When the filament sensor error count is larger then FSENSOR_ERR_MAX, pauses print, tries to move filament back and forth.
//! If there is still no plausible signal from filament sensor plans M600 (Filament change).
void fsensor_update(void)
{
#ifdef PAT9125
if (fsensor_enabled && fsensor_watch_runout && (fsensor_err_cnt > FSENSOR_ERR_MAX) && ( ! fsensor_m600_enqueued) )
{
bool autoload_enabled_tmp = fsensor_autoload_enabled;
fsensor_autoload_enabled = false;
bool oq_meassure_enabled_tmp = fsensor_oq_meassure_enabled;
fsensor_oq_meassure_enabled = true;
fsensor_stop_and_save_print();
fsensor_err_cnt = 0;
fsensor_oq_meassure_start(0);
enquecommand_front_P((PSTR("G1 E-3 F200")));
process_commands();
KEEPALIVE_STATE(IN_HANDLER);
cmdqueue_pop_front();
st_synchronize();
enquecommand_front_P((PSTR("G1 E3 F200")));
process_commands();
KEEPALIVE_STATE(IN_HANDLER);
cmdqueue_pop_front();
st_synchronize();
uint8_t err_cnt = fsensor_err_cnt;
fsensor_oq_meassure_stop();
bool err = false;
err |= (err_cnt > 1);
err |= (fsensor_oq_er_sum > 2);
err |= (fsensor_oq_yd_sum < (4 * FSENSOR_OQ_MIN_YD));
if (!err)
{
printf_P(PSTR("fsensor_err_cnt = 0\n"));
fsensor_restore_print_and_continue();
}
else
{
fsensor_enque_M600();
fsensor_watch_runout = false;
}
fsensor_autoload_enabled = autoload_enabled_tmp;
fsensor_oq_meassure_enabled = oq_meassure_enabled_tmp;
}
#else //PAT9125
if ((digitalRead(IR_SENSOR_PIN) == 1) && CHECK_FSENSOR && fsensor_enabled && ir_sensor_detected && ( ! fsensor_m600_enqueued) )
{
fsensor_stop_and_save_print();
fsensor_enque_M600();
}
#endif //PAT9125
}

68
Firmware/fsensor.h Normal file
View File

@ -0,0 +1,68 @@
//! @file
#ifndef FSENSOR_H
#define FSENSOR_H
#include <inttypes.h>
//! minimum meassured chunk length in steps
extern int16_t fsensor_chunk_len;
// enable/disable flag
extern bool fsensor_enabled;
// not responding flag
extern bool fsensor_not_responding;
//enable/disable quality meassurement
extern bool fsensor_oq_meassure_enabled;
//! @name save restore printing
//! @{
extern void fsensor_stop_and_save_print(void);
//! restore print - restore position and heatup to original temperature
extern void fsensor_restore_print_and_continue(void);
//! @}
//! initialize
extern void fsensor_init(void);
//! @name enable/disable
//! @{
extern bool fsensor_enable(void);
extern void fsensor_disable(void);
//! @}
//autoload feature enabled
extern bool fsensor_autoload_enabled;
extern void fsensor_autoload_set(bool State);
extern void fsensor_update(void);
#ifdef PAT9125
//! setup pin-change interrupt
extern void fsensor_setup_interrupt(void);
//! @name autoload support
//! @{
extern void fsensor_autoload_check_start(void);
extern void fsensor_autoload_check_stop(void);
#endif //PAT9125
extern bool fsensor_check_autoload(void);
//! @}
//! @name optical quality measurement support
//! @{
extern void fsensor_oq_meassure_set(bool State);
extern void fsensor_oq_meassure_start(uint8_t skip);
extern void fsensor_oq_meassure_stop(void);
extern bool fsensor_oq_result(void);
//! @}
#include "planner.h"
//! @name callbacks from stepper
//! @{
extern void fsensor_st_block_begin(block_t* bl);
extern void fsensor_st_block_chunk(block_t* bl, int cnt);
//! @}
#endif //FSENSOR_H

180
Firmware/heatbed_pwm.cpp Normal file
View File

@ -0,0 +1,180 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include "io_atmega2560.h"
// All this is about silencing the heat bed, as it behaves like a loudspeaker.
// Basically, we want the PWM heating switched at 30Hz (or so) which is a well ballanced
// frequency for both power supply units (i.e. both PSUs are reasonably silent).
// The only trouble is the rising or falling edge of bed heating - that creates an audible click.
// This audible click may be suppressed by making the rising or falling edge NOT sharp.
// Of course, making non-sharp edges in digital technology is not easy, but there is a solution.
// It is possible to do a fast PWM sequence with duty starting from 0 to 255.
// Doing this at higher frequency than the bed "loudspeaker" can handle makes the click barely audible.
// Technically:
// timer0 is set to fast PWM mode at 62.5kHz (timer0 is linked to the bed heating pin) (zero prescaler)
// To keep the bed switching at 30Hz - we don't want the PWM running at 62kHz all the time
// since it would burn the heatbed's MOSFET:
// 16MHz/256 levels of PWM duty gives us 62.5kHz
// 62.5kHz/256 gives ~244Hz, that is still too fast - 244/8 gives ~30Hz, that's what we need
// So the automaton runs atop of inner 8 (or 16) cycles.
// The finite automaton is running in the ISR(TIMER0_OVF_vect)
// 2019-08-14 update: the original algorithm worked very well, however there were 2 regressions:
// 1. 62kHz ISR requires considerable amount of processing power,
// USB transfer speed dropped by 20%, which was most notable when doing short G-code segments.
// 2. Some users reported TLed PSU started clicking when running at 120V/60Hz.
// This looks like the original algorithm didn't maintain base PWM 30Hz, but only 15Hz
// To address both issues, there is an improved approach based on the idea of leveraging
// different CLK prescalers in some automaton states - i.e. when holding LOW or HIGH on the output pin,
// we don't have to clock 62kHz, but we can increase the CLK prescaler for these states to 8 (or even 64).
// That shall result in the ISR not being called that much resulting in regained performance
// Theoretically this is relatively easy, however one must be very carefull handling the AVR's timer
// control registers correctly, especially setting them in a correct order.
// Some registers are double buffered, some changes are applied in next cycles etc.
// The biggest problem was with the CLK prescaler itself - this circuit is shared among almost all timers,
// we don't want to reset the prescaler counted value when transiting among automaton states.
// Resetting the prescaler would make the PWM more precise, right now there are temporal segments
// of variable period ranging from 0 to 7 62kHz ticks - that's logical, the timer must "sync"
// to the new slower CLK after setting the slower prescaler value.
// In our application, this isn't any significant problem and may be ignored.
// Doing changes in timer's registers non-correctly results in artefacts on the output pin
// - it can toggle unnoticed, which will result in bed clicking again.
// That's why there are special transition states ZERO_TO_RISE and ONE_TO_FALL, which enable the
// counter change its operation atomically and without artefacts on the output pin.
// The resulting signal on the output pin was checked with an osciloscope.
// If there are any change requirements in the future, the signal must be checked with an osciloscope again,
// ad-hoc changes may completely screw things up!
///! Definition off finite automaton states
enum class States : uint8_t {
ZERO_START = 0,///< entry point of the automaton - reads the soft_pwm_bed value for the next whole PWM cycle
ZERO, ///< steady 0 (OFF), no change for the whole period
ZERO_TO_RISE, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
RISE, ///< 16 fast PWM cycles with increasing duty up to steady ON
RISE_TO_ONE, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
ONE, ///< steady 1 (ON), no change for the whole period
ONE_TO_FALL, ///< metastate allowing the timer change its state atomically without artefacts on the output pin
FALL, ///< 16 fast PWM cycles with decreasing duty down to steady OFF
FALL_TO_ZERO ///< metastate allowing the timer change its state atomically without artefacts on the output pin
};
///! Inner states of the finite automaton
static States state = States::ZERO_START;
///! Fast PWM counter is used in the RISE and FALL states (62.5kHz)
static uint8_t slowCounter = 0;
///! Slow PWM counter is used in the ZERO and ONE states (62.5kHz/8 or 64)
static uint8_t fastCounter = 0;
///! PWM counter for the whole cycle - a cache for soft_pwm_bed
static uint8_t pwm = 0;
///! The slow PWM duty for the next 30Hz cycle
///! Set in the whole firmware at various places
extern unsigned char soft_pwm_bed;
/// fastMax - how many fast PWM steps to do in RISE and FALL states
/// 16 is a good compromise between silenced bed ("smooth" edges)
/// and not burning the switching MOSFET
static const uint8_t fastMax = 16;
/// Scaler 16->256 for fast PWM
static const uint8_t fastShift = 4;
/// Increment slow PWM counter by slowInc every ZERO or ONE state
/// This allows for fine-tuning the basic PWM switching frequency
/// A possible further optimization - use a 64 prescaler (instead of 8)
/// increment slowCounter by 1
/// but use less bits of soft PWM - something like soft_pwm_bed >> 2
/// that may further reduce the CPU cycles required by the bed heating automaton
/// Due to the nature of bed heating the reduced PID precision may not be a major issue, however doing 8x less ISR(timer0_ovf) may significantly improve the performance
static const uint8_t slowInc = 1;
ISR(TIMER0_OVF_vect) // timer compare interrupt service routine
{
switch(state){
case States::ZERO_START:
pwm = soft_pwm_bed << 1;// expecting soft_pwm_bed to be 7bit!
if( pwm != 0 ){
state = States::ZERO; // do nothing, let it tick once again after the 30Hz period
}
break;
case States::ZERO: // end of state ZERO - we'll either stay in ZERO or change to RISE
// In any case update our cache of pwm value for the next whole cycle from soft_pwm_bed
slowCounter += slowInc; // this does software timer_clk/256 or less (depends on slowInc)
if( slowCounter > pwm ){
return;
} // otherwise moving towards RISE
state = States::ZERO_TO_RISE; // and finalize the change in a transitional state RISE0
break;
// even though it may look like the ZERO state may be glued together with the ZERO_TO_RISE, don't do it
// the timer must tick once more in order to get rid of occasional output pin toggles.
case States::ZERO_TO_RISE: // special state for handling transition between prescalers and switching inverted->non-inverted fast-PWM without toggling the output pin.
// It must be done in consequent steps, otherwise the pin will get flipped up and down during one PWM cycle.
// Also beware of the correct sequence of the following timer control registers initialization - it really matters!
state = States::RISE; // prepare for standard RISE cycles
fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
TCNT0 = 255; // force overflow on the next clock cycle
TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
TCCR0A &= ~(1 << COM0B0); // Clear OC0B on Compare Match, set OC0B at BOTTOM (non-inverting mode)
break;
case States::RISE:
OCR0B = (fastMax - fastCounter) << fastShift;
if( fastCounter ){
--fastCounter;
} else { // end of RISE cycles, changing into state ONE
state = States::RISE_TO_ONE;
OCR0B = 255; // full duty
TCNT0 = 254; // make the timer overflow in the next cycle
// @@TODO these constants are still subject to investigation
}
break;
case States::RISE_TO_ONE:
state = States::ONE;
OCR0B = 255; // full duty
TCNT0 = 255; // make the timer overflow in the next cycle
TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
break;
case States::ONE: // state ONE - we'll either stay in ONE or change to FALL
OCR0B = 255;
slowCounter += slowInc; // this does software timer_clk/256 or less
if( slowCounter < pwm ){
return;
}
if( (soft_pwm_bed << 1) >= (255 - slowInc - 1) ){ //@@TODO simplify & explain
// if slowInc==2, soft_pwm == 251 will be the first to do short drops to zero. 252 will keep full heating
return; // want full duty for the next ONE cycle again - so keep on heating and just wait for the next timer ovf
}
// otherwise moving towards FALL
// @@TODO it looks like ONE_TO_FALL isn't necessary, there are no artefacts at all
state = States::ONE;//_TO_FALL;
// TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
// break;
// case States::ONE_TO_FALL:
// OCR0B = 255; // zero duty
state=States::FALL;
fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
TCNT0 = 255; // force overflow on the next clock cycle
TCCR0B = (1 << CS00); // change prescaler to 1, i.e. 62.5kHz
// must switch to inverting mode already here, because it takes a whole PWM cycle and it would make a "1" at the end of this pwm cycle
// COM0B1 remains set both in inverting and non-inverting mode
TCCR0A |= (1 << COM0B0); // inverting mode
break;
case States::FALL:
OCR0B = (fastMax - fastCounter) << fastShift; // this is the same as in RISE, because now we are setting the zero part of duty due to inverting mode
//TCCR0A |= (1 << COM0B0); // already set in ONE_TO_FALL
if( fastCounter ){
--fastCounter;
} else { // end of FALL cycles, changing into state ZERO
state = States::FALL_TO_ZERO;
TCNT0 = 128; //@@TODO again - need to wait long enough to propagate the timer state changes
OCR0B = 255;
}
break;
case States::FALL_TO_ZERO:
state = States::ZERO_START; // go to read new soft_pwm_bed value for the next cycle
TCNT0 = 128;
OCR0B = 255;
TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
break;
}
}

374
Firmware/io_atmega2560.h Normal file
View File

@ -0,0 +1,374 @@
//io_atmega2560.h
#ifndef _IO_ATMEGA2560
#define _IO_ATMEGA2560
#define __PIN_P0 PINE
#define __PIN_P1 PINE
#define __PIN_P2 PINE
#define __PIN_P3 PINE
#define __PIN_P4 PING
#define __PIN_P5 PINE
#define __PIN_P6 PINH
#define __PIN_P7 PINH
#define __PIN_P8 PINH
#define __PIN_P9 PINH
#define __PIN_P10 PINB
#define __PIN_P11 PINB
#define __PIN_P12 PINB
#define __PIN_P13 PINB
#define __PIN_P14 PINJ
#define __PIN_P15 PINJ
#define __PIN_P16 PINH
#define __PIN_P17 PINH
#define __PIN_P18 PIND
#define __PIN_P19 PIND
#define __PIN_P20 PIND
#define __PIN_P21 PIND
#define __PIN_P22 PINA
#define __PIN_P23 PINA
#define __PIN_P24 PINA
#define __PIN_P25 PINA
#define __PIN_P26 PINA
#define __PIN_P27 PINA
#define __PIN_P28 PINA
#define __PIN_P29 PINA
#define __PIN_P30 PINC
#define __PIN_P31 PINC
#define __PIN_P32 PINC
#define __PIN_P33 PINC
#define __PIN_P34 PINC
#define __PIN_P35 PINC
#define __PIN_P36 PINC
#define __PIN_P37 PINC
#define __PIN_P38 PIND
#define __PIN_P39 PING
#define __PIN_P40 PING
#define __PIN_P41 PING
#define __PIN_P42 PINL
#define __PIN_P43 PINL
#define __PIN_P44 PINL
#define __PIN_P45 PINL
#define __PIN_P46 PINL
#define __PIN_P47 PINL
#define __PIN_P48 PINL
#define __PIN_P49 PINL
#define __PIN_P50 PINB
#define __PIN_P51 PINB
#define __PIN_P52 PINB
#define __PIN_P53 PINB
#define __PIN_P54 PINF
#define __PIN_P55 PINF
#define __PIN_P56 PINF
#define __PIN_P57 PINF
#define __PIN_P58 PINF
#define __PIN_P59 PINF
#define __PIN_P60 PINF
#define __PIN_P61 PINF
#define __PIN_P62 PINK
#define __PIN_P63 PINK
#define __PIN_P64 PINK
#define __PIN_P65 PINK
#define __PIN_P66 PINK
#define __PIN_P67 PINK
#define __PIN_P68 PINK
#define __PIN_P69 PINK
#define __PIN_P70 PING
#define __PIN_P71 PING
#define __PIN_P72 PINJ
#define __PIN_P73 PINJ
#define __PIN_P74 PINJ
#define __PIN_P75 PINJ
#define __PIN_P76 PINJ
#define __PIN_P77 PINJ
#define __PIN_P78 PINE
#define __PIN_P79 PINE
#define __PIN_P80 PINE
#define __PIN_P81 PIND
#define __PIN_P82 PIND
#define __PIN_P83 PIND
#define __PIN_P84 PINH
#define __PIN_P85 PINH
#define __PORT_P0 PORTE
#define __PORT_P1 PORTE
#define __PORT_P2 PORTE
#define __PORT_P3 PORTE
#define __PORT_P4 PORTG
#define __PORT_P5 PORTE
#define __PORT_P6 PORTH
#define __PORT_P7 PORTH
#define __PORT_P8 PORTH
#define __PORT_P9 PORTH
#define __PORT_P10 PORTB
#define __PORT_P11 PORTB
#define __PORT_P12 PORTB
#define __PORT_P13 PORTB
#define __PORT_P14 PORTJ
#define __PORT_P15 PORTJ
#define __PORT_P16 PORTH
#define __PORT_P17 PORTH
#define __PORT_P18 PORTD
#define __PORT_P19 PORTD
#define __PORT_P20 PORTD
#define __PORT_P21 PORTD
#define __PORT_P22 PORTA
#define __PORT_P23 PORTA
#define __PORT_P24 PORTA
#define __PORT_P25 PORTA
#define __PORT_P26 PORTA
#define __PORT_P27 PORTA
#define __PORT_P28 PORTA
#define __PORT_P29 PORTA
#define __PORT_P30 PORTC
#define __PORT_P31 PORTC
#define __PORT_P32 PORTC
#define __PORT_P33 PORTC
#define __PORT_P34 PORTC
#define __PORT_P35 PORTC
#define __PORT_P36 PORTC
#define __PORT_P37 PORTC
#define __PORT_P38 PORTD
#define __PORT_P39 PORTG
#define __PORT_P40 PORTG
#define __PORT_P41 PORTG
#define __PORT_P42 PORTL
#define __PORT_P43 PORTL
#define __PORT_P44 PORTL
#define __PORT_P45 PORTL
#define __PORT_P46 PORTL
#define __PORT_P47 PORTL
#define __PORT_P48 PORTL
#define __PORT_P49 PORTL
#define __PORT_P50 PORTB
#define __PORT_P51 PORTB
#define __PORT_P52 PORTB
#define __PORT_P53 PORTB
#define __PORT_P54 PORTF
#define __PORT_P55 PORTF
#define __PORT_P56 PORTF
#define __PORT_P57 PORTF
#define __PORT_P58 PORTF
#define __PORT_P59 PORTF
#define __PORT_P60 PORTF
#define __PORT_P61 PORTF
#define __PORT_P62 PORTK
#define __PORT_P63 PORTK
#define __PORT_P64 PORTK
#define __PORT_P65 PORTK
#define __PORT_P66 PORTK
#define __PORT_P67 PORTK
#define __PORT_P68 PORTK
#define __PORT_P69 PORTK
#define __PORT_P70 PORTG
#define __PORT_P71 PORTG
#define __PORT_P72 PORTJ
#define __PORT_P73 PORTJ
#define __PORT_P74 PORTJ
#define __PORT_P75 PORTJ
#define __PORT_P76 PORTJ
#define __PORT_P77 PORTJ
#define __PORT_P78 PORTE
#define __PORT_P79 PORTE
#define __PORT_P80 PORTE
#define __PORT_P81 PORTD
#define __PORT_P82 PORTD
#define __PORT_P83 PORTD
#define __PORT_P84 PORTH
#define __PORT_P85 PORTH
#define __DDR_P0 DDRE
#define __DDR_P1 DDRE
#define __DDR_P2 DDRE
#define __DDR_P3 DDRE
#define __DDR_P4 DDRG
#define __DDR_P5 DDRE
#define __DDR_P6 DDRH
#define __DDR_P7 DDRH
#define __DDR_P8 DDRH
#define __DDR_P9 DDRH
#define __DDR_P10 DDRB
#define __DDR_P11 DDRB
#define __DDR_P12 DDRB
#define __DDR_P13 DDRB
#define __DDR_P14 DDRJ
#define __DDR_P15 DDRJ
#define __DDR_P16 DDRH
#define __DDR_P17 DDRH
#define __DDR_P18 DDRD
#define __DDR_P19 DDRD
#define __DDR_P20 DDRD
#define __DDR_P21 DDRD
#define __DDR_P22 DDRA
#define __DDR_P23 DDRA
#define __DDR_P24 DDRA
#define __DDR_P25 DDRA
#define __DDR_P26 DDRA
#define __DDR_P27 DDRA
#define __DDR_P28 DDRA
#define __DDR_P29 DDRA
#define __DDR_P30 DDRC
#define __DDR_P31 DDRC
#define __DDR_P32 DDRC
#define __DDR_P33 DDRC
#define __DDR_P34 DDRC
#define __DDR_P35 DDRC
#define __DDR_P36 DDRC
#define __DDR_P37 DDRC
#define __DDR_P38 DDRD
#define __DDR_P39 DDRG
#define __DDR_P40 DDRG
#define __DDR_P41 DDRG
#define __DDR_P42 DDRL
#define __DDR_P43 DDRL
#define __DDR_P44 DDRL
#define __DDR_P45 DDRL
#define __DDR_P46 DDRL
#define __DDR_P47 DDRL
#define __DDR_P48 DDRL
#define __DDR_P49 DDRL
#define __DDR_P50 DDRB
#define __DDR_P51 DDRB
#define __DDR_P52 DDRB
#define __DDR_P53 DDRB
#define __DDR_P54 DDRF
#define __DDR_P55 DDRF
#define __DDR_P56 DDRF
#define __DDR_P57 DDRF
#define __DDR_P58 DDRF
#define __DDR_P59 DDRF
#define __DDR_P60 DDRF
#define __DDR_P61 DDRF
#define __DDR_P62 DDRK
#define __DDR_P63 DDRK
#define __DDR_P64 DDRK
#define __DDR_P65 DDRK
#define __DDR_P66 DDRK
#define __DDR_P67 DDRK
#define __DDR_P68 DDRK
#define __DDR_P69 DDRK
#define __DDR_P70 DDRG
#define __DDR_P71 DDRG
#define __DDR_P72 DDRJ
#define __DDR_P73 DDRJ
#define __DDR_P74 DDRJ
#define __DDR_P75 DDRJ
#define __DDR_P76 DDRJ
#define __DDR_P77 DDRJ
#define __DDR_P78 DDRE
#define __DDR_P79 DDRE
#define __DDR_P80 DDRE
#define __DDR_P81 DDRD
#define __DDR_P82 DDRD
#define __DDR_P83 DDRD
#define __DDR_P84 DDRH
#define __DDR_P85 DDRH
#define __BIT_P0 0
#define __BIT_P1 1
#define __BIT_P2 4
#define __BIT_P3 5
#define __BIT_P4 5
#define __BIT_P5 3
#define __BIT_P6 3
#define __BIT_P7 4
#define __BIT_P8 5
#define __BIT_P9 6
#define __BIT_P10 4
#define __BIT_P11 5
#define __BIT_P12 6
#define __BIT_P13 7
#define __BIT_P14 1
#define __BIT_P15 0
#define __BIT_P16 0
#define __BIT_P17 1
#define __BIT_P18 3
#define __BIT_P19 2
#define __BIT_P20 1
#define __BIT_P21 0
#define __BIT_P22 0
#define __BIT_P23 1
#define __BIT_P24 2
#define __BIT_P25 3
#define __BIT_P26 4
#define __BIT_P27 5
#define __BIT_P28 6
#define __BIT_P29 7
#define __BIT_P30 7
#define __BIT_P31 6
#define __BIT_P32 5
#define __BIT_P33 4
#define __BIT_P34 3
#define __BIT_P35 2
#define __BIT_P36 1
#define __BIT_P37 0
#define __BIT_P38 7
#define __BIT_P39 2
#define __BIT_P40 1
#define __BIT_P41 0
#define __BIT_P42 7
#define __BIT_P43 6
#define __BIT_P44 5
#define __BIT_P45 4
#define __BIT_P46 3
#define __BIT_P47 2
#define __BIT_P48 1
#define __BIT_P49 0
#define __BIT_P50 3
#define __BIT_P51 2
#define __BIT_P52 1
#define __BIT_P53 0
#define __BIT_P54 0
#define __BIT_P55 1
#define __BIT_P56 2
#define __BIT_P57 3
#define __BIT_P58 4
#define __BIT_P59 5
#define __BIT_P60 6
#define __BIT_P61 7
#define __BIT_P62 0
#define __BIT_P63 1
#define __BIT_P64 2
#define __BIT_P65 3
#define __BIT_P66 4
#define __BIT_P67 5
#define __BIT_P68 6
#define __BIT_P69 7
#define __BIT_P70 4
#define __BIT_P71 3
#define __BIT_P72 2
#define __BIT_P73 3
#define __BIT_P74 7
#define __BIT_P75 4
#define __BIT_P76 5
#define __BIT_P77 6
#define __BIT_P78 2
#define __BIT_P79 6
#define __BIT_P80 7
#define __BIT_P81 4
#define __BIT_P82 5
#define __BIT_P83 6
#define __BIT_P84 2
#define __BIT_P85 7
#define __BIT(pin) __BIT_P##pin
#define __MSK(pin) (1 << __BIT(pin))
#define __PIN(pin) __PIN_P##pin
#define __PORT(pin) __PORT_P##pin
#define __DDR(pin) __DDR_P##pin
#define PIN(pin) __PIN(pin)
#define PORT(pin) __PORT(pin)
#define DDR(pin) __DDR(pin)
#define PIN_INP(pin) DDR(pin) &= ~__MSK(pin)
#define PIN_OUT(pin) DDR(pin) |= __MSK(pin)
#define PIN_CLR(pin) PORT(pin) &= ~__MSK(pin)
#define PIN_SET(pin) PORT(pin) |= __MSK(pin)
#define PIN_VAL(pin, val) if (val) PIN_SET(pin); else PIN_CLR(pin);
#define PIN_GET(pin) (PIN(pin) & __MSK(pin))
#define PIN_INQ(pin) (PORT(pin) & __MSK(pin))
#endif //_IO_ATMEGA2560

272
Firmware/language.c Normal file
View File

@ -0,0 +1,272 @@
//language.c
#include "language.h"
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include "bootapp.h"
#include "Configuration.h"
#include "pins.h"
#ifdef W25X20CL
#include "w25x20cl.h"
#endif //W25X20CL
// Currently active language selection.
uint8_t lang_selected = 0;
#if (LANG_MODE == 0) //primary language only
uint8_t lang_select(uint8_t lang) { return 0; }
uint8_t lang_get_count() { return 1; }
uint16_t lang_get_code(uint8_t lang) { return LANG_CODE_EN; }
const char* lang_get_name_by_code(uint16_t code) { return _n("English"); }
void lang_reset(void) { }
uint8_t lang_is_selected(void) { return 1; }
#else //(LANG_MODE == 0) //secondary languages in progmem or xflash
//reserved xx kbytes for secondary language table
const char _SEC_LANG[LANG_SIZE_RESERVED] PROGMEM_I2 = "_SEC_LANG";
//primary language signature
const uint32_t _PRI_LANG_SIGNATURE[1] __attribute__((section(".progmem0"))) = {0xffffffff};
//lang_table pointer
lang_table_t* lang_table = 0;
const char* lang_get_translation(const char* s)
{
if (lang_selected == 0) return s + 2; //primary language selected, return orig. str.
if (lang_table == 0) return s + 2; //sec. lang table not found, return orig. str.
uint16_t ui = pgm_read_word(((uint16_t*)s)); //read string id
if (ui == 0xffff) return s + 2; //translation not found, return orig. str.
ui = pgm_read_word(((uint16_t*)(((char*)lang_table + 16 + ui*2)))); //read relative offset
if (pgm_read_byte(((uint8_t*)((char*)lang_table + ui))) == 0) //read first character
return s + 2;//zero length string == not translated, return orig. str.
return (const char*)((char*)lang_table + ui); //return calculated pointer
}
uint8_t lang_select(uint8_t lang)
{
if (lang == LANG_ID_PRI) //primary language
{
lang_table = 0;
lang_selected = lang;
}
#ifdef W25X20CL
if (lang_get_code(lang) == lang_get_code(LANG_ID_SEC)) lang = LANG_ID_SEC;
if (lang == LANG_ID_SEC) //current secondary language
{
if (pgm_read_dword(((uint32_t*)_SEC_LANG_TABLE)) == LANG_MAGIC) //magic valid
{
if (lang_check(_SEC_LANG_TABLE))
if (pgm_read_dword(((uint32_t*)(_SEC_LANG_TABLE + 12))) == pgm_read_dword(((uint32_t*)(_PRI_LANG_SIGNATURE)))) //signature valid
{
lang_table = (lang_table_t*)(_SEC_LANG_TABLE); // set table pointer
lang_selected = lang; // set language id
}
}
}
#else //W25X20CL
if (lang == LANG_ID_SEC)
{
uint16_t table = _SEC_LANG_TABLE;
if (pgm_read_dword(((uint32_t*)table)) == LANG_MAGIC) //magic valid
{
if (lang_check(table))
if (pgm_read_dword(((uint32_t*)(table + 12))) == pgm_read_dword(((uint32_t*)(_PRI_LANG_SIGNATURE)))) //signature valid
{
lang_table = (lang_table_t*)table; // set table pointer
lang_selected = lang; // set language id
}
}
}
#endif //W25X20CL
if (lang_selected == lang)
{
eeprom_update_byte((unsigned char*)EEPROM_LANG, lang_selected);
return 1;
}
return 0;
}
uint8_t lang_check(uint16_t addr)
{
uint16_t sum = 0;
uint16_t size = pgm_read_word((uint16_t*)(addr + 4));
uint16_t lt_sum = pgm_read_word((uint16_t*)(addr + 8));
uint16_t i; for (i = 0; i < size; i++)
sum += (uint16_t)pgm_read_byte((uint8_t*)(addr + i)) << ((i & 1)?0:8);
sum -= lt_sum; //subtract checksum
sum = (sum >> 8) | ((sum & 0xff) << 8); //swap bytes
return (sum == lt_sum);
}
uint8_t lang_get_count()
{
if (pgm_read_dword(((uint32_t*)(_PRI_LANG_SIGNATURE))) == 0xffffffff)
return 1; //signature not set - only primary language will be available
#ifdef W25X20CL
W25X20CL_SPI_ENTER();
uint8_t count = 2; //count = 1+n (primary + secondary + all in xflash)
uint32_t addr = 0x00000; //start of xflash
lang_table_header_t header; //table header structure
while (1)
{
w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
if (header.magic != LANG_MAGIC) break; //break if magic not valid
addr += header.size; //calc address of next table
count++; //inc counter
}
#else //W25X20CL
uint16_t table = _SEC_LANG_TABLE;
uint8_t count = 1; //count = 1 (primary)
while (pgm_read_dword(((uint32_t*)table)) == LANG_MAGIC) //magic valid
{
table += pgm_read_word((uint16_t*)(table + 4));
count++;
}
#endif //W25X20CL
return count;
}
uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* offset)
{
if (lang == LANG_ID_PRI) return 0; //primary lang not supported for this function
#ifdef W25X20CL
if (lang == LANG_ID_SEC)
{
uint16_t ui = _SEC_LANG_TABLE; //table pointer
memcpy_P(header, (lang_table_t*)(_SEC_LANG_TABLE), sizeof(lang_table_header_t)); //read table header from progmem
if (offset) *offset = ui;
return (header->magic == LANG_MAGIC)?1:0; //return 1 if magic valid
}
W25X20CL_SPI_ENTER();
uint32_t addr = 0x00000; //start of xflash
lang--;
while (1)
{
w25x20cl_rd_data(addr, (uint8_t*)(header), sizeof(lang_table_header_t)); //read table header from xflash
if (header->magic != LANG_MAGIC) break; //break if not valid
if (offset) *offset = addr;
if (--lang == 0) return 1;
addr += header->size; //calc address of next table
}
#else //W25X20CL
if (lang == LANG_ID_SEC)
{
uint16_t ui = _SEC_LANG_TABLE; //table pointer
memcpy_P(header, (lang_table_header_t*)ui, sizeof(lang_table_header_t)); //read table header from progmem
if (offset) *offset = ui;
return (header->magic == LANG_MAGIC)?1:0; //return 1 if magic valid
}
#endif //W25X20CL
return 0;
}
uint16_t lang_get_code(uint8_t lang)
{
if (lang == LANG_ID_PRI) return LANG_CODE_EN; //primary lang = EN
#ifdef W25X20CL
if (lang == LANG_ID_SEC)
{
uint16_t ui = _SEC_LANG_TABLE; //table pointer
if (pgm_read_dword(((uint32_t*)(ui + 0))) != LANG_MAGIC) return LANG_CODE_XX; //magic not valid
return pgm_read_word(((uint32_t*)(ui + 10))); //return lang code from progmem
}
W25X20CL_SPI_ENTER();
uint32_t addr = 0x00000; //start of xflash
lang_table_header_t header; //table header structure
lang--;
while (1)
{
w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
if (header.magic != LANG_MAGIC) break; //break if not valid
if (--lang == 0) return header.code;
addr += header.size; //calc address of next table
}
#else //W25X20CL
uint16_t table = _SEC_LANG_TABLE;
uint8_t count = 1; //count = 1 (primary)
while (pgm_read_dword((uint32_t*)table) == LANG_MAGIC) //magic valid
{
if (count == lang) return pgm_read_word(((uint16_t*)(table + 10))); //read language code
table += pgm_read_word((uint16_t*)(table + 4));
count++;
}
#endif //W25X20CL
return LANG_CODE_XX;
}
const char* lang_get_name_by_code(uint16_t code)
{
switch (code)
{
case LANG_CODE_EN: return _n("English");
case LANG_CODE_CZ: return _n("Cestina");
case LANG_CODE_DE: return _n("Deutsch");
case LANG_CODE_ES: return _n("Espanol");
case LANG_CODE_FR: return _n("Francais");
case LANG_CODE_IT: return _n("Italiano");
case LANG_CODE_PL: return _n("Polski");
}
return _n("??");
}
void lang_reset(void)
{
lang_selected = 0;
eeprom_update_byte((unsigned char*)EEPROM_LANG, LANG_ID_FORCE_SELECTION);
}
uint8_t lang_is_selected(void)
{
uint8_t lang_eeprom = eeprom_read_byte((unsigned char*)EEPROM_LANG);
return (lang_eeprom != LANG_ID_FORCE_SELECTION) && (lang_eeprom == lang_selected);
}
#ifdef DEBUG_SEC_LANG
#include <stdio.h>
const char* lang_get_sec_lang_str_by_id(uint16_t id)
{
uint16_t ui = _SEC_LANG_TABLE; //table pointer
return ui + pgm_read_word(((uint16_t*)(ui + 16 + id * 2))); //read relative offset and return calculated pointer
}
uint16_t lang_print_sec_lang(FILE* out)
{
printf_P(_n("&_SEC_LANG = 0x%04x\n"), &_SEC_LANG);
printf_P(_n("sizeof(_SEC_LANG) = 0x%04x\n"), sizeof(_SEC_LANG));
uint16_t ptr_lang_table0 = ((uint16_t)(&_SEC_LANG) + 0xff) & 0xff00;
printf_P(_n("&_lang_table0 = 0x%04x\n"), ptr_lang_table0);
uint32_t _lt_magic = pgm_read_dword(((uint32_t*)(ptr_lang_table0 + 0)));
uint16_t _lt_size = pgm_read_word(((uint16_t*)(ptr_lang_table0 + 4)));
uint16_t _lt_count = pgm_read_word(((uint16_t*)(ptr_lang_table0 + 6)));
uint16_t _lt_chsum = pgm_read_word(((uint16_t*)(ptr_lang_table0 + 8)));
uint16_t _lt_resv0 = pgm_read_word(((uint16_t*)(ptr_lang_table0 + 10)));
uint32_t _lt_resv1 = pgm_read_dword(((uint32_t*)(ptr_lang_table0 + 12)));
printf_P(_n(" _lt_magic = 0x%08lx %S\n"), _lt_magic, (_lt_magic==LANG_MAGIC)?_n("OK"):_n("NA"));
printf_P(_n(" _lt_size = 0x%04x (%d)\n"), _lt_size, _lt_size);
printf_P(_n(" _lt_count = 0x%04x (%d)\n"), _lt_count, _lt_count);
printf_P(_n(" _lt_chsum = 0x%04x\n"), _lt_chsum);
printf_P(_n(" _lt_resv0 = 0x%04x\n"), _lt_resv0);
printf_P(_n(" _lt_resv1 = 0x%08lx\n"), _lt_resv1);
if (_lt_magic != LANG_MAGIC) return 0;
puts_P(_n(" strings:\n"));
uint16_t ui = _SEC_LANG_TABLE; //table pointer
for (ui = 0; ui < _lt_count; ui++)
fprintf_P(out, _n(" %3d %S\n"), ui, lang_get_sec_lang_str_by_id(ui));
return _lt_count;
}
#endif //DEBUG_SEC_LANG
#endif //(LANG_MODE == 0)
void lang_boot_update_start(uint8_t lang)
{
uint8_t cnt = lang_get_count();
if ((lang < 2) || (lang > cnt)) return; //only languages from xflash can be selected
bootapp_reboot_user0(lang << 4);
}

151
Firmware/language.h Normal file
View File

@ -0,0 +1,151 @@
/** @file */
//language.h
#ifndef LANGUAGE_H
#define LANGUAGE_H
#include "config.h"
#include <inttypes.h>
//#include <stdio.h>
#define PROTOCOL_VERSION "1.0"
#ifndef CUSTOM_MENDEL_NAME
#define MACHINE_NAME "Mendel"
#endif
#ifndef MACHINE_UUID
#define MACHINE_UUID "00000000-0000-0000-0000-000000000000"
#endif
#define MSG_FW_VERSION "Firmware"
#define STRINGIFY_(n) #n
#define STRINGIFY(n) STRINGIFY_(n)
#if (LANG_MODE == 0) //primary language only
#define PROGMEM_I2 __attribute__((section(".progmem0")))
#define PROGMEM_I1 __attribute__((section(".progmem1")))
#define PROGMEM_N1 __attribute__((section(".progmem2")))
#define _I(s) (__extension__({static const char __c[] PROGMEM_I1 = s; &__c[0];}))
#define ISTR(s) s
#define _i(s) _I(s)
#define _T(s) s
#else //(LANG_MODE == 0)
// section .loc_sec (originaly .progmem0) will be used for localized translated strings
#define PROGMEM_I2 __attribute__((section(".loc_sec")))
// section .loc_pri (originaly .progmem1) will be used for localized strings in english
#define PROGMEM_I1 __attribute__((section(".loc_pri")))
// section .noloc (originaly progmem2) will be used for not localized strings in english
#define PROGMEM_N1 __attribute__((section(".noloc")))
#define _I(s) (__extension__({static const char __c[] PROGMEM_I1 = "\xff\xff" s; &__c[0];}))
#define ISTR(s) "\xff\xff" s
#define _i(s) lang_get_translation(_I(s))
#define _T(s) lang_get_translation(s)
#endif //(LANG_MODE == 0)
#define _N(s) (__extension__({static const char __c[] PROGMEM_N1 = s; &__c[0];}))
#define _n(s) _N(s)
/** @brief lang_table_header_t structure - (size= 16byte) */
typedef struct
{
uint32_t magic; //+0
uint16_t size; //+4
uint16_t count; //+6
uint16_t checksum; //+8
uint16_t code; //+10
uint32_t signature; //+12
} lang_table_header_t;
/** @brief lang_table_t structure - (size= 16byte + 2*count) */
typedef struct
{
lang_table_header_t header;
uint16_t table[];
} lang_table_t;
/** @name Language indices into their particular symbol tables.*/
///@{
#define LANG_ID_PRI 0
#define LANG_ID_SEC 1
///@}
/** @def LANG_ID_FORCE_SELECTION
* @brief Language is not defined and it shall be selected from the menu.*/
#define LANG_ID_FORCE_SELECTION 254
/** @def LANG_ID_UNDEFINED
* @brief Language is not defined on a virgin RAMBo board. */
#define LANG_ID_UNDEFINED 255
/** @def LANG_ID_DEFAULT
* @brief Default language ID, if no language is selected. */
#define LANG_ID_DEFAULT LANG_ID_PRI
/** @def LANG_MAGIC
* @brief Magic number at begin of lang table. */
#define LANG_MAGIC 0x4bb45aa5
/** @name Language codes (ISO639-1)*/
///@{
#define LANG_CODE_XX 0x3f3f //!<'??'
#define LANG_CODE_EN 0x656e //!<'en'
#define LANG_CODE_CZ 0x6373 //!<'cs'
#define LANG_CODE_DE 0x6465 //!<'de'
#define LANG_CODE_ES 0x6573 //!<'es'
#define LANG_CODE_FR 0x6672 //!<'fr'
#define LANG_CODE_IT 0x6974 //!<'it'
#define LANG_CODE_PL 0x706c //!<'pl'
///@}
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
/** @brief Currectly active language selection.*/
extern uint8_t lang_selected;
#if (LANG_MODE != 0)
extern const char _SEC_LANG[LANG_SIZE_RESERVED];
extern const char* lang_get_translation(const char* s);
/** @def _SEC_LANG_TABLE
* @brief Align table to start of 256 byte page */
#define _SEC_LANG_TABLE ((((uint16_t)&_SEC_LANG) + 0x00ff) & 0xff00)
#endif //(LANG_MODE != 0)
/** @brief selects language, eeprom is updated in case of success */
extern uint8_t lang_select(uint8_t lang);
/** @brief performs checksum test of secondary language data */
extern uint8_t lang_check(uint16_t addr);
/** @return total number of languages (primary + all in xflash) */
extern uint8_t lang_get_count(void);
/** @brief reads lang table header and offset in xflash or progmem */
extern uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* offset);
/** @brief reads lang code from xflash or progmem */
extern uint16_t lang_get_code(uint8_t lang);
/** @return localized language name (text for menu item) */
extern const char* lang_get_name_by_code(uint16_t code);
/** @brief reset language to "LANG_ID_FORCE_SELECTION", epprom is updated */
extern void lang_reset(void);
/** @retval 1 language is selected */
extern uint8_t lang_is_selected(void);
#ifdef DEBUG_SEC_LANG
extern const char* lang_get_sec_lang_str_by_id(uint16_t id);
extern uint16_t lang_print_sec_lang(FILE* out);
#endif //DEBUG_SEC_LANG
extern void lang_boot_update_start(uint8_t lang);
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)
#define CAT2(_s1, _s2) _s1
#define CAT4(_s1, _s2, _s3, _s4) _s1
#include "messages.h"
#endif //LANGUAGE_H

1004
Firmware/lcd.cpp Normal file

File diff suppressed because it is too large Load Diff

216
Firmware/lcd.h Normal file
View File

@ -0,0 +1,216 @@
//! @file
#ifndef _LCD_H
#define _LCD_H
#include <inttypes.h>
#include <stdio.h>
#include "Timer.h"
extern FILE _lcdout;
#define lcdout (&_lcdout)
extern void lcd_init(void);
extern void lcd_refresh(void);
extern void lcd_refresh_noclear(void);
extern void lcd_clear(void);
extern void lcd_home(void);
/*extern void lcd_no_display(void);
extern void lcd_display(void);
extern void lcd_no_blink(void);
extern void lcd_blink(void);
extern void lcd_no_cursor(void);
extern void lcd_cursor(void);
extern void lcd_scrollDisplayLeft(void);
extern void lcd_scrollDisplayRight(void);
extern void lcd_leftToRight(void);
extern void lcd_rightToLeft(void);
extern void lcd_autoscroll(void);
extern void lcd_no_autoscroll(void);*/
extern void lcd_set_cursor(uint8_t col, uint8_t row);
extern void lcd_createChar_P(uint8_t, const uint8_t*);
extern int lcd_putc(int c);
extern int lcd_puts_P(const char* str);
extern int lcd_puts_at_P(uint8_t c, uint8_t r, const char* str);
extern int lcd_printf_P(const char* format, ...);
extern void lcd_space(uint8_t n);
extern void lcd_printNumber(unsigned long n, uint8_t base);
extern void lcd_printFloat(double number, uint8_t digits);
extern void lcd_print(const char*);
extern void lcd_print(char, int = 0);
extern void lcd_print(unsigned char, int = 0);
extern void lcd_print(int, int = 10);
extern void lcd_print(unsigned int, int = 10);
extern void lcd_print(long, int = 10);
extern void lcd_print(unsigned long, int = 10);
extern void lcd_print(double, int = 2);
//! @brief Clear screen
#define ESC_2J "\x1b[2J"
//! @brief Show cursor
#define ESC_25h "\x1b[?25h"
//! @brief Hide cursor
#define ESC_25l "\x1b[?25l"
//! @brief Set cursor to
//! @param c column
//! @param r row
#define ESC_H(c,r) "\x1b["#r";"#c"H"
#define LCD_UPDATE_INTERVAL 100
#define LCD_TIMEOUT_TO_STATUS 30000ul //!< Generic timeout to status screen in ms, when no user action.
#define LCD_TIMEOUT_TO_STATUS_BABYSTEP_Z 90000ul //!< Specific timeout for lcd_babystep_z screen in ms.
typedef void (*lcd_longpress_func_t)(void);
typedef void (*lcd_charsetup_func_t)(void);
typedef void (*lcd_lcdupdate_func_t)(void);
//Set to none-zero when the LCD needs to draw, decreased after every draw. Set to 2 in LCD routines so the LCD gets at least 1 full redraw (first redraw is partial)
extern uint8_t lcd_draw_update;
extern int32_t lcd_encoder;
extern uint8_t lcd_encoder_bits;
// lcd_encoder_diff is updated from interrupt context and added to lcd_encoder every LCD update
extern int8_t lcd_encoder_diff;
//the last checked lcd_buttons in a bit array.
extern uint8_t lcd_buttons;
extern uint8_t lcd_button_pressed;
extern uint8_t lcd_update_enabled;
extern LongTimer lcd_timeoutToStatus;
extern uint32_t lcd_next_update_millis;
extern uint8_t lcd_status_update_delay;
extern lcd_longpress_func_t lcd_longpress_func;
extern lcd_charsetup_func_t lcd_charsetup_func;
extern lcd_lcdupdate_func_t lcd_lcdupdate_func;
extern uint8_t lcd_clicked(void);
extern void lcd_beeper_quick_feedback(void);
//Cause an LCD refresh, and give the user visual or audible feedback that something has happened
extern void lcd_quick_feedback(void);
extern void lcd_update(uint8_t lcdDrawUpdateOverride);
extern void lcd_update_enable(uint8_t enabled);
extern void lcd_buttons_update(void);
//! @brief Helper class to temporarily disable LCD updates
//!
//! When constructed (on stack), original state state of lcd_update_enabled is stored
//! and LCD updates are disabled.
//! When destroyed (gone out of scope), original state of LCD update is restored.
//! It has zero overhead compared to storing bool saved = lcd_update_enabled
//! and calling lcd_update_enable(false) and lcd_update_enable(saved).
class LcdUpdateDisabler
{
public:
LcdUpdateDisabler(): m_updateEnabled(lcd_update_enabled)
{
lcd_update_enable(false);
}
~LcdUpdateDisabler()
{
lcd_update_enable(m_updateEnabled);
}
private:
bool m_updateEnabled;
};
////////////////////////////////////
// Setup button and encode mappings for each panel (into 'lcd_buttons' variable
//
// This is just to map common functions (across different panels) onto the same
// macro name. The mapping is independent of whether the button is directly connected or
// via a shift/i2c register.
#define BLEN_B 1
#define BLEN_A 0
#define EN_B (1<<BLEN_B) // The two encoder pins are connected through BTN_EN1 and BTN_EN2
#define EN_A (1<<BLEN_A)
#define BLEN_C 2
#define EN_C (1<<BLEN_C)
//! @brief Was button clicked?
//!
//! Doesn't consume button click event. See lcd_clicked()
//! for function consuming the event.
//!
//! Generally is used in non-modal menus.
//!
//! @retval 0 button was not clicked
//! @retval 1 button was clicked
#define LCD_CLICKED (lcd_buttons&EN_C)
////////////////////////
// Setup Rotary Encoder Bit Values (for two pin encoders to indicate movement)
// These values are independent of which pins are used for EN_A and EN_B indications
// The rotary encoder part is also independent to the chipset used for the LCD
#define encrot0 0
#define encrot1 2
#define encrot2 3
#define encrot3 1
//Custom characters defined in the first 8 characters of the LCD
#define LCD_STR_BEDTEMP "\x00"
#define LCD_STR_DEGREE "\x01"
#define LCD_STR_THERMOMETER "\x02"
#define LCD_STR_UPLEVEL "\x03"
#define LCD_STR_REFRESH "\x04"
#define LCD_STR_FOLDER "\x05"
#define LCD_STR_FEEDRATE "\x06"
#define LCD_STR_CLOCK "\x07"
#define LCD_STR_ARROW_UP "\x0B"
#define LCD_STR_ARROW_DOWN "\x01"
#define LCD_STR_ARROW_RIGHT "\x7E" //from the default character set
extern void lcd_set_custom_characters(void);
extern void lcd_set_custom_characters_arrows(void);
extern void lcd_set_custom_characters_progress(void);
extern void lcd_set_custom_characters_nextpage(void);
extern void lcd_set_custom_characters_degree(void);
//! @brief Consume click event
inline void lcd_consume_click()
{
lcd_button_pressed = 0;
lcd_buttons &= 0xff^EN_C;
}
#endif //_LCD_H

29
Firmware/le.sh Normal file
View File

@ -0,0 +1,29 @@
# line ending management script
# CRLF - windows default ('\r\n')
# LF - unix default ('\n')
# arguments:
# ?crlf - print all .cpp and .h files with CRLF line endings
# ?lf - print all .cpp and .h files with LF line endings
# crlf - replace line endings in all .cpp and .h files to CRLF
# lf - replace line endings in all .cpp and .h files to LF
if [ "$1" == "?crlf" ] || [ $# -eq 0 ]; then
echo 'cpp and h files with CRLF line endings:'
find {*.cpp,*.h} -not -type d -exec file "{}" ";" | grep CRLF | sed 's/:.*//g'
elif [ "$1" == "?lf" ]; then
echo 'cpp and h files with LF line endings:'
find {*.cpp,*.h} -not -type d -exec file "{}" ";" | grep -v CRLF | sed 's/:.*//g'
fi
if [ "$1" == "crlf" ]; then
echo 'replacing LF with CRLF in all cpp and h files:'
find {*.cpp,*.h} -not -type d -exec file "{}" ";" | grep -v CRLF | sed 's/:.*//g' | while read fn; do
echo "$fn"
sed -i 's/$/\r/g' $fn
done
elif [ "$1" == "lf" ]; then
echo 'replacing CRLF with LF in all cpp and h files:'
find {*.cpp,*.h} -not -type d -exec file "{}" ";" | grep CRLF | sed 's/:.*//g' | while read fn; do
echo "$fn"
sed -i 's/\r\n/\n/g' $fn
done
fi

511
Firmware/menu.cpp Normal file
View File

@ -0,0 +1,511 @@
//menu.cpp
#include "menu.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <avr/pgmspace.h>
#include "lcd.h"
#include "Configuration.h"
#include "Marlin.h"
#include "ultralcd.h"
#include "language.h"
#include "static_assert.h"
#include "sound.h"
extern int32_t lcd_encoder;
#define MENU_DEPTH_MAX 7
static menu_record_t menu_stack[MENU_DEPTH_MAX];
uint8_t menu_data[MENU_DATA_SIZE];
#ifndef __AVR__
#error "menu_data is non-portable to non 8bit processor"
#endif
uint8_t menu_depth = 0;
uint8_t menu_block_entering_on_serious_errors = SERIOUS_ERR_NONE;
uint8_t menu_line = 0;
uint8_t menu_item = 0;
uint8_t menu_row = 0;
uint8_t menu_top = 0;
uint8_t menu_clicked = 0;
uint8_t menu_entering = 0;
uint8_t menu_leaving = 0;
menu_func_t menu_menu = 0;
static_assert(sizeof(menu_data)>= sizeof(menu_data_edit_t),"menu_data_edit_t doesn't fit into menu_data");
void menu_goto(menu_func_t menu, const uint32_t encoder, const bool feedback, bool reset_menu_state)
{
asm("cli");
if (menu_menu != menu)
{
menu_menu = menu;
lcd_encoder = encoder;
asm("sei");
if (reset_menu_state)
{
// Resets the global shared C union.
// This ensures, that the menu entered will find out, that it shall initialize itself.
memset(&menu_data, 0, sizeof(menu_data));
}
if (feedback) lcd_quick_feedback();
}
else
asm("sei");
}
void menu_start(void)
{
if (lcd_encoder > 0x8000) lcd_encoder = 0;
if (lcd_encoder < 0)
{
lcd_encoder = 0;
Sound_MakeSound(e_SOUND_TYPE_BlindAlert);
}
if (lcd_encoder < menu_top)
menu_top = lcd_encoder;
menu_line = menu_top;
menu_clicked = LCD_CLICKED;
}
void menu_end(void)
{
if (lcd_encoder >= menu_item)
{
lcd_encoder = menu_item - 1;
Sound_MakeSound(e_SOUND_TYPE_BlindAlert);
}
if (((uint8_t)lcd_encoder) >= menu_top + LCD_HEIGHT)
{
menu_top = lcd_encoder - LCD_HEIGHT + 1;
lcd_draw_update = 1;
menu_line = menu_top - 1;
menu_row = -1;
}
}
void menu_back(uint8_t nLevel)
{
menu_depth = ((menu_depth > nLevel) ? (menu_depth - nLevel) : 0);
menu_goto(menu_stack[menu_depth].menu, menu_stack[menu_depth].position, true, true);
}
void menu_back(void)
{
menu_back(1);
}
static void menu_back_no_reset(void)
{
if (menu_depth > 0)
{
menu_depth--;
menu_goto(menu_stack[menu_depth].menu, menu_stack[menu_depth].position, true, false);
}
}
void menu_back_if_clicked(void)
{
if (lcd_clicked())
menu_back();
}
void menu_back_if_clicked_fb(void)
{
if (lcd_clicked())
{
lcd_quick_feedback();
menu_back();
}
}
void menu_submenu(menu_func_t submenu)
{
if (menu_depth < MENU_DEPTH_MAX)
{
menu_stack[menu_depth].menu = menu_menu;
menu_stack[menu_depth++].position = lcd_encoder;
menu_goto(submenu, 0, true, true);
}
}
static void menu_submenu_no_reset(menu_func_t submenu)
{
if (menu_depth < MENU_DEPTH_MAX)
{
menu_stack[menu_depth].menu = menu_menu;
menu_stack[menu_depth++].position = lcd_encoder;
menu_goto(submenu, 0, true, false);
}
}
uint8_t menu_item_ret(void)
{
lcd_beeper_quick_feedback();
lcd_draw_update = 2;
lcd_button_pressed = false;
return 1;
}
/*
int menu_draw_item_printf_P(char type_char, const char* format, ...)
{
va_list args;
va_start(args, format);
int ret = 0;
lcd_set_cursor(0, menu_row);
if (lcd_encoder == menu_item)
lcd_print('>');
else
lcd_print(' ');
int cnt = vfprintf_P(lcdout, format, args);
for (int i = cnt; i < 18; i++)
lcd_print(' ');
lcd_print(type_char);
va_end(args);
return ret;
}
*/
static char menu_selection_mark(){
return (lcd_encoder == menu_item)?'>':' ';
}
static void menu_draw_item_puts_P(char type_char, const char* str)
{
lcd_set_cursor(0, menu_row);
lcd_printf_P(PSTR("%c%-18.18S%c"), menu_selection_mark(), str, type_char);
}
//! @brief Format sheet name
//!
//! @param[in] sheet_E Sheet in EEPROM
//! @param[out] buffer for formatted output
void menu_format_sheet_E(const Sheet &sheet_E, SheetFormatBuffer &buffer)
{
uint_least8_t index = sprintf_P(buffer.c, PSTR("%.10S "), _T(MSG_SHEET));
eeprom_read_block(&(buffer.c[index]), sheet_E.name, 7);
//index += 7;
buffer.c[index + 7] = '\0';
}
//! @brief Format sheet name in select menu
//!
//! @param[in] sheet_E Sheet in EEPROM
//! @param[out] buffer for formatted output
void menu_format_sheet_select_E(const Sheet &sheet_E, SheetFormatBuffer &buffer)
{
uint_least8_t index = sprintf_P(buffer.c,PSTR("%-9.9S["), _T(MSG_SHEET));
eeprom_read_block(&(buffer.c[index]), sheet_E.name, sizeof(sheet_E.name)/sizeof(sheet_E.name[0]));
for (const uint_least8_t start = index; static_cast<uint_least8_t>(index - start) < sizeof(sheet_E.name)/sizeof(sheet_E.name[0]); ++index)
{
if (buffer.c[index] == '\0') break;
}
buffer.c[index] = ']';
buffer.c[index + 1] = '\0';
}
static void menu_draw_item_select_sheet_E(char type_char, const Sheet &sheet)
{
lcd_set_cursor(0, menu_row);
SheetFormatBuffer buffer;
menu_format_sheet_select_E(sheet, buffer);
lcd_printf_P(PSTR("%c%-18.18s%c"), menu_selection_mark(), buffer.c, type_char);
}
static void menu_draw_item_puts_E(char type_char, const Sheet &sheet)
{
lcd_set_cursor(0, menu_row);
SheetFormatBuffer buffer;
menu_format_sheet_E(sheet, buffer);
lcd_printf_P(PSTR("%c%-18.18s%c"), menu_selection_mark(), buffer.c, type_char);
}
static void menu_draw_item_puts_P(char type_char, const char* str, char num)
{
lcd_set_cursor(0, menu_row);
lcd_printf_P(PSTR("%c%-.16S "), menu_selection_mark(), str);
lcd_putc(num);
lcd_set_cursor(19, menu_row);
lcd_putc(type_char);
}
/*
int menu_draw_item_puts_P_int16(char type_char, const char* str, int16_t val, )
{
lcd_set_cursor(0, menu_row);
int cnt = lcd_printf_P(PSTR("%c%-18S%c"), (lcd_encoder == menu_item)?'>':' ', str, type_char);
return cnt;
}
*/
void menu_item_dummy(void)
{
menu_item++;
}
uint8_t menu_item_text_P(const char* str)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_puts_P(' ', str);
if (menu_clicked && (lcd_encoder == menu_item))
return menu_item_ret();
}
menu_item++;
return 0;
}
uint8_t menu_item_submenu_P(const char* str, menu_func_t submenu)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_puts_P(LCD_STR_ARROW_RIGHT[0], str);
if (menu_clicked && (lcd_encoder == menu_item))
{
menu_submenu(submenu);
return menu_item_ret();
}
}
menu_item++;
return 0;
}
uint8_t menu_item_submenu_E(const Sheet &sheet, menu_func_t submenu)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_puts_E(LCD_STR_ARROW_RIGHT[0], sheet);
if (menu_clicked && (lcd_encoder == menu_item))
{
menu_submenu(submenu);
return menu_item_ret();
}
}
menu_item++;
return 0;
}
uint8_t menu_item_function_E(const Sheet &sheet, menu_func_t func)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_select_sheet_E(' ', sheet);
if (menu_clicked && (lcd_encoder == menu_item))
{
menu_clicked = false;
lcd_consume_click();
lcd_update_enabled = 0;
if (func) func();
lcd_update_enabled = 1;
return menu_item_ret();
}
}
menu_item++;
return 0;
}
uint8_t menu_item_back_P(const char* str)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_puts_P(LCD_STR_UPLEVEL[0], str);
if (menu_clicked && (lcd_encoder == menu_item))
{
menu_back();
return menu_item_ret();
}
}
menu_item++;
return 0;
}
uint8_t menu_item_function_P(const char* str, menu_func_t func)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_puts_P(' ', str);
if (menu_clicked && (lcd_encoder == menu_item))
{
menu_clicked = false;
lcd_consume_click();
lcd_update_enabled = 0;
if (func) func();
lcd_update_enabled = 1;
return menu_item_ret();
}
}
menu_item++;
return 0;
}
//! @brief Menu item function taking single parameter
//!
//! Ideal for numbered lists calling functions with number parameter.
//! @param str Item caption
//! @param number aditional character to be added after str, e.g. number
//! @param func pointer to function taking uint8_t with no return value
//! @param fn_par value to be passed to function
//! @retval 0
//! @retval 1 Item was clicked
uint8_t menu_item_function_P(const char* str, char number, void (*func)(uint8_t), uint8_t fn_par)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_puts_P(' ', str, number);
if (menu_clicked && (lcd_encoder == menu_item))
{
menu_clicked = false;
lcd_consume_click();
lcd_update_enabled = 0;
if (func) func(fn_par);
lcd_update_enabled = 1;
return menu_item_ret();
}
}
menu_item++;
return 0;
}
uint8_t menu_item_gcode_P(const char* str, const char* str_gcode)
{
if (menu_item == menu_line)
{
if (lcd_draw_update) menu_draw_item_puts_P(' ', str);
if (menu_clicked && (lcd_encoder == menu_item))
{
if (str_gcode) enquecommand_P(str_gcode);
return menu_item_ret();
}
}
menu_item++;
return 0;
}
const char menu_20x_space[] PROGMEM = " ";
const char menu_fmt_int3[] PROGMEM = "%c%.15S:%s%3d";
const char menu_fmt_float31[] PROGMEM = "%-12.12S%+8.1f";
const char menu_fmt_float13[] PROGMEM = "%c%-13.13S%+5.3f";
const char menu_fmt_float13off[] PROGMEM = "%c%-13.13S%6.6S";
template<typename T>
static void menu_draw_P(char chr, const char* str, int16_t val);
template<>
void menu_draw_P<int16_t*>(char chr, const char* str, int16_t val)
{
int text_len = strlen_P(str);
if (text_len > 15) text_len = 15;
char spaces[21];
strcpy_P(spaces, menu_20x_space);
if (val <= -100) spaces[15 - text_len - 1] = 0;
else spaces[15 - text_len] = 0;
lcd_printf_P(menu_fmt_int3, chr, str, spaces, val);
}
template<>
void menu_draw_P<uint8_t*>(char chr, const char* str, int16_t val)
{
menu_data_edit_t* _md = (menu_data_edit_t*)&(menu_data[0]);
float factor = 1.0f + static_cast<float>(val) / 1000.0f;
if (val <= _md->minEditValue)
{
lcd_printf_P(menu_fmt_float13off, chr, str, _i(" [off]"));
}
else
{
lcd_printf_P(menu_fmt_float13, chr, str, factor);
}
}
//! @brief Draw up to 10 chars of text and a float number in format from +0.0 to +12345.0. The increased range is necessary
//! for displaying large values of extruder positions, which caused text overflow in the previous implementation.
//!
//! @param str string label to print
//! @param val value to print aligned to the right side of the display
//!
//! Implementation comments:
//! The text needs to come with a colon ":", this function does not append it anymore.
//! That resulted in a much shorter implementation (234628B -> 234476B)
//! There are similar functions around which may be shortened in a similar way
void menu_draw_float31(const char* str, float val)
{
lcd_printf_P(menu_fmt_float31, str, val);
}
//! @brief Draw up to 14 chars of text and a float number in format +1.234
//!
//! @param str string label to print
//! @param val value to print aligned to the right side of the display
//!
//! Implementation comments:
//! This function uses similar optimization principles as menu_draw_float31
//! (i.e. str must include a ':' at its end)
//! FLASH usage dropped 234476B -> 234392B
//! Moreover, this function gets inlined in the final code, so removing it doesn't really help ;)
void menu_draw_float13(const char* str, float val)
{
lcd_printf_P(menu_fmt_float13, ' ', str, val);
}
template <typename T>
static void _menu_edit_P(void)
{
menu_data_edit_t* _md = (menu_data_edit_t*)&(menu_data[0]);
if (lcd_draw_update)
{
if (lcd_encoder < _md->minEditValue) lcd_encoder = _md->minEditValue;
if (lcd_encoder > _md->maxEditValue) lcd_encoder = _md->maxEditValue;
lcd_set_cursor(0, 1);
menu_draw_P<T>(' ', _md->editLabel, (int)lcd_encoder);
}
if (LCD_CLICKED)
{
*((T)(_md->editValue)) = lcd_encoder;
menu_back_no_reset();
}
}
template <typename T>
uint8_t menu_item_edit_P(const char* str, T pval, int16_t min_val, int16_t max_val)
{
menu_data_edit_t* _md = (menu_data_edit_t*)&(menu_data[0]);
if (menu_item == menu_line)
{
if (lcd_draw_update)
{
lcd_set_cursor(0, menu_row);
menu_draw_P<T>(menu_selection_mark(), str, *pval);
}
if (menu_clicked && (lcd_encoder == menu_item))
{
menu_submenu_no_reset(_menu_edit_P<T>);
_md->editLabel = str;
_md->editValue = pval;
_md->minEditValue = min_val;
_md->maxEditValue = max_val;
lcd_encoder = *pval;
return menu_item_ret();
}
}
menu_item++;
return 0;
}
template uint8_t menu_item_edit_P<int16_t*>(const char* str, int16_t *pval, int16_t min_val, int16_t max_val);
template uint8_t menu_item_edit_P<uint8_t*>(const char* str, uint8_t *pval, int16_t min_val, int16_t max_val);
#undef _menu_data

149
Firmware/menu.h Normal file
View File

@ -0,0 +1,149 @@
//menu.h
#ifndef _MENU_H
#define _MENU_H
#include <inttypes.h>
#include "eeprom.h"
#define MENU_DATA_SIZE 32
//Function pointer to menu functions.
typedef void (*menu_func_t)(void);
typedef struct
{
menu_func_t menu;
int8_t position;
} menu_record_t;
typedef struct
{
//Variables used when editing values.
const char* editLabel;
void* editValue;
int32_t minEditValue;
int32_t maxEditValue;
} menu_data_edit_t;
extern uint8_t menu_data[MENU_DATA_SIZE];
extern uint8_t menu_depth;
//! definition of serious errors possibly blocking the main menu
//! Use them as bit mask, so that the code may set various errors at the same time
enum ESeriousErrors {
SERIOUS_ERR_NONE = 0,
SERIOUS_ERR_MINTEMP_HEATER = 0x01,
SERIOUS_ERR_MINTEMP_BED = 0x02
}; // and possibly others in the future.
//! this is a flag for disabling entering the main menu. If this is set
//! to anything != 0, the only the main status screen will be shown on the
//! LCD and the user will be prevented from entering the menu.
//! Now used only to block doing anything with the printer when there is
//! the infamous MINTEMP error (SERIOUS_ERR_MINTEMP).
extern uint8_t menu_block_entering_on_serious_errors;
//! a pair of macros for manipulating the serious errors
//! a c++ class would have been better
#define menu_set_serious_error(x) menu_block_entering_on_serious_errors |= x;
#define menu_unset_serious_error(x) menu_block_entering_on_serious_errors &= ~x;
#define menu_is_serious_error(x) (menu_block_entering_on_serious_errors & x) != 0
extern uint8_t menu_line;
extern uint8_t menu_item;
extern uint8_t menu_row;
//scroll offset in the current menu
extern uint8_t menu_top;
extern uint8_t menu_clicked;
extern uint8_t menu_entering;
extern uint8_t menu_leaving;
//function pointer to the currently active menu
extern menu_func_t menu_menu;
extern void menu_goto(menu_func_t menu, const uint32_t encoder, const bool feedback, bool reset_menu_state);
#define MENU_BEGIN() menu_start(); for(menu_row = 0; menu_row < LCD_HEIGHT; menu_row++, menu_line++) { menu_item = 0;
void menu_start(void);
#define MENU_END() menu_end(); }
extern void menu_end(void);
extern void menu_back(void);
extern void menu_back(uint8_t nLevel);
extern void menu_back_if_clicked(void);
extern void menu_back_if_clicked_fb(void);
extern void menu_submenu(menu_func_t submenu);
extern uint8_t menu_item_ret(void);
//extern int menu_draw_item_printf_P(char type_char, const char* format, ...);
//int menu_draw_item_puts_P_int16(char type_char, const char* str, int16_t val, );
#define MENU_ITEM_DUMMY() menu_item_dummy()
extern void menu_item_dummy(void);
#define MENU_ITEM_TEXT_P(str) do { if (menu_item_text_P(str)) return; } while (0)
extern uint8_t menu_item_text_P(const char* str);
#define MENU_ITEM_SUBMENU_P(str, submenu) do { if (menu_item_submenu_P(str, submenu)) return; } while (0)
extern uint8_t menu_item_submenu_P(const char* str, menu_func_t submenu);
#define MENU_ITEM_SUBMENU_E(sheet, submenu) do { if (menu_item_submenu_E(sheet, submenu)) return; } while (0)
extern uint8_t menu_item_submenu_E(const Sheet &sheet, menu_func_t submenu);
#define MENU_ITEM_FUNCTION_E(sheet, submenu) do { if (menu_item_function_E(sheet, submenu)) return; } while (0)
extern uint8_t menu_item_function_E(const Sheet &sheet, menu_func_t func);
#define MENU_ITEM_BACK_P(str) do { if (menu_item_back_P(str)) return; } while (0)
extern uint8_t menu_item_back_P(const char* str);
// leaving menu - this condition must be immediately before MENU_ITEM_BACK_P
#define ON_MENU_LEAVE(func) do { if (((menu_item == menu_line) && menu_clicked && (lcd_encoder == menu_item)) || menu_leaving){ func } } while (0)
#define MENU_ITEM_FUNCTION_P(str, func) do { if (menu_item_function_P(str, func)) return; } while (0)
extern uint8_t menu_item_function_P(const char* str, menu_func_t func);
#define MENU_ITEM_FUNCTION_NR_P(str, number, func, fn_par) do { if (menu_item_function_P(str, number, func, fn_par)) return; } while (0)
extern uint8_t menu_item_function_P(const char* str, char number, void (*func)(uint8_t), uint8_t fn_par);
#define MENU_ITEM_GCODE_P(str, str_gcode) do { if (menu_item_gcode_P(str, str_gcode)) return; } while (0)
extern uint8_t menu_item_gcode_P(const char* str, const char* str_gcode);
extern const char menu_fmt_int3[];
extern const char menu_fmt_float31[];
extern const char menu_fmt_float13[];
extern void menu_draw_float31(const char* str, float val);
extern void menu_draw_float13(const char* str, float val);
struct SheetFormatBuffer
{
char c[19];
};
extern void menu_format_sheet_E(const Sheet &sheet_E, SheetFormatBuffer &buffer);
#define MENU_ITEM_EDIT_int3_P(str, pval, minval, maxval) do { if (menu_item_edit_P(str, pval, minval, maxval)) return; } while (0)
//#define MENU_ITEM_EDIT_int3_P(str, pval, minval, maxval) MENU_ITEM_EDIT(int3, str, pval, minval, maxval)
template <typename T>
extern uint8_t menu_item_edit_P(const char* str, T pval, int16_t min_val, int16_t max_val);
#endif //_MENU_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,216 @@
#ifndef MESH_BED_CALIBRATION_H
#define MESH_BED_CALIBRATION_H
#define BED_ZERO_REF_X (- 22.f + X_PROBE_OFFSET_FROM_EXTRUDER) // -22 + 23 = 1
#define BED_ZERO_REF_Y (- 0.6f + Y_PROBE_OFFSET_FROM_EXTRUDER + 4.f) // -0.6 + 5 + 4 = 8.4
#ifdef HEATBED_V2
#define BED_X0 (2.f - BED_ZERO_REF_X) //1
#define BED_Y0 (9.4f - BED_ZERO_REF_Y) //1
#define BED_Xn (206.f - BED_ZERO_REF_X) //205
#define BED_Yn (213.4f - BED_ZERO_REF_Y) //205
#else
#define BED_X0 (13.f - BED_ZERO_REF_X)
#define BED_Y0 (8.4f - BED_ZERO_REF_Y)
#define BED_Xn (216.f - BED_ZERO_REF_X)
#define BED_Yn (202.4f - BED_ZERO_REF_Y)
#endif //not HEATBED_V2
#define BED_X(i, n) ((float)i * (BED_Xn - BED_X0) / (n - 1) + BED_X0)
#define BED_Y(i, n) ((float)i * (BED_Yn - BED_Y0) / (n - 1) + BED_Y0)
// Exact positions of the print head above the bed reference points, in the world coordinates.
// The world coordinates match the machine coordinates only in case, when the machine
// is built properly, the end stops are at the correct positions and the axes are perpendicular.
extern const float bed_ref_points_4[] PROGMEM;
extern const float bed_skew_angle_mild;
extern const float bed_skew_angle_extreme;
// Is the world2machine correction activated?
enum World2MachineCorrectionMode
{
WORLD2MACHINE_CORRECTION_NONE = 0,
WORLD2MACHINE_CORRECTION_SHIFT = 1,
WORLD2MACHINE_CORRECTION_SKEW = 2,
};
extern uint8_t world2machine_correction_mode;
// 2x2 transformation matrix from the world coordinates to the machine coordinates.
// Corrects for the rotation and skew of the machine axes.
// Used by the planner's plan_buffer_line() and plan_set_position().
extern float world2machine_rotation_and_skew[2][2];
extern float world2machine_rotation_and_skew_inv[2][2];
// Shift of the machine zero point, in the machine coordinates.
extern float world2machine_shift[2];
extern void world2machine_reset();
extern void world2machine_revert_to_uncorrected();
extern void world2machine_initialize();
extern void world2machine_read_valid(float vec_x[2], float vec_y[2], float cntr[2]);
extern void world2machine_update_current();
inline void world2machine(float &x, float &y)
{
if (world2machine_correction_mode == WORLD2MACHINE_CORRECTION_NONE) {
// No correction.
} else {
if (world2machine_correction_mode & WORLD2MACHINE_CORRECTION_SKEW) {
// Firs the skew & rotation correction.
float out_x = world2machine_rotation_and_skew[0][0] * x + world2machine_rotation_and_skew[0][1] * y;
float out_y = world2machine_rotation_and_skew[1][0] * x + world2machine_rotation_and_skew[1][1] * y;
x = out_x;
y = out_y;
}
if (world2machine_correction_mode & WORLD2MACHINE_CORRECTION_SHIFT) {
// Then add the offset.
x += world2machine_shift[0];
y += world2machine_shift[1];
}
}
}
inline void world2machine(const float &x, const float &y, float &out_x, float &out_y)
{
out_x = x;
out_y = y;
world2machine(out_x, out_y);
}
inline void machine2world(float x, float y, float &out_x, float &out_y)
{
if (world2machine_correction_mode == WORLD2MACHINE_CORRECTION_NONE) {
// No correction.
out_x = x;
out_y = y;
} else {
if (world2machine_correction_mode & WORLD2MACHINE_CORRECTION_SHIFT) {
// Then add the offset.
x -= world2machine_shift[0];
y -= world2machine_shift[1];
}
if (world2machine_correction_mode & WORLD2MACHINE_CORRECTION_SKEW) {
// Firs the skew & rotation correction.
out_x = world2machine_rotation_and_skew_inv[0][0] * x + world2machine_rotation_and_skew_inv[0][1] * y;
out_y = world2machine_rotation_and_skew_inv[1][0] * x + world2machine_rotation_and_skew_inv[1][1] * y;
}
}
}
inline void machine2world(float &x, float &y)
{
if (world2machine_correction_mode == WORLD2MACHINE_CORRECTION_NONE) {
// No correction.
} else {
if (world2machine_correction_mode & WORLD2MACHINE_CORRECTION_SHIFT) {
// Then add the offset.
x -= world2machine_shift[0];
y -= world2machine_shift[1];
}
if (world2machine_correction_mode & WORLD2MACHINE_CORRECTION_SKEW) {
// Firs the skew & rotation correction.
float out_x = world2machine_rotation_and_skew_inv[0][0] * x + world2machine_rotation_and_skew_inv[0][1] * y;
float out_y = world2machine_rotation_and_skew_inv[1][0] * x + world2machine_rotation_and_skew_inv[1][1] * y;
x = out_x;
y = out_y;
}
}
}
inline bool world2machine_clamp(float &x, float &y)
{
bool clamped = false;
float tmpx, tmpy;
world2machine(x, y, tmpx, tmpy);
if (tmpx < X_MIN_POS) {
tmpx = X_MIN_POS;
clamped = true;
}
if (tmpy < Y_MIN_POS) {
tmpy = Y_MIN_POS;
clamped = true;
}
if (tmpx > X_MAX_POS) {
tmpx = X_MAX_POS;
clamped = true;
}
if (tmpy > Y_MAX_POS) {
tmpy = Y_MAX_POS;
clamped = true;
}
if (clamped)
machine2world(tmpx, tmpy, x, y);
return clamped;
}
extern bool find_bed_induction_sensor_point_z(float minimum_z = -10.f, uint8_t n_iter = 3, int verbosity_level = 0);
extern bool find_bed_induction_sensor_point_xy(int verbosity_level = 0);
extern void go_home_with_z_lift();
/**
* @brief Bed skew and offest detection result
*
* Positive or zero: ok
* Negative: failed
*/
enum BedSkewOffsetDetectionResultType {
// Detection failed, some point was not found.
BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND = -1, //!< Point not found.
BED_SKEW_OFFSET_DETECTION_FITTING_FAILED = -2, //!< Fitting failed
// Detection finished with success.
BED_SKEW_OFFSET_DETECTION_PERFECT = 0, //!< Perfect.
BED_SKEW_OFFSET_DETECTION_SKEW_MILD = 1, //!< Mildly skewed.
BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME = 2 //!< Extremely skewed.
};
extern BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level, uint8_t &too_far_mask);
#ifndef NEW_XYZCAL
extern BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8_t verbosity_level, uint8_t &too_far_mask);
#endif //NEW_XYZCAL
extern bool sample_mesh_and_store_reference();
extern void reset_bed_offset_and_skew();
extern bool is_bed_z_jitter_data_valid();
// Scan the mesh bed induction points one by one by a left-right zig-zag movement,
// write the trigger coordinates to the serial line.
// Useful for visualizing the behavior of the bed induction detector.
extern bool scan_bed_induction_points(int8_t verbosity_level);
// Load Z babystep value from the EEPROM into babystepLoadZ,
// but don't apply it through the planner. This is useful on wake up
// after power panic, when it is expected, that the baby step has been already applied.
extern void babystep_load();
// Apply Z babystep value from the EEPROM through the planner.
extern void babystep_apply();
// Undo the current Z babystep value.
extern void babystep_undo();
// Reset the current babystep counter without moving the axes.
extern void babystep_reset();
extern void count_xyz_details(float (&distanceMin)[2]);
extern bool sample_z();
/*
typedef enum
{
e_MBL_FAST, e_MBL_OPTIMAL, e_MBL_PREC
} e_MBL_TYPE;
*/
//extern e_MBL_TYPE e_mbl_type;
//extern void mbl_mode_set();
//extern void mbl_mode_init();
extern void mbl_settings_init();
extern bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy, uint8_t meas_points, bool zigzag);
extern void mbl_interpolation(uint8_t meas_points);
#endif /* MESH_BED_CALIBRATION_H */

View File

@ -0,0 +1,107 @@
#include "mesh_bed_leveling.h"
#include "mesh_bed_calibration.h"
#include "Configuration.h"
#ifdef MESH_BED_LEVELING
mesh_bed_leveling mbl;
mesh_bed_leveling::mesh_bed_leveling() { reset(); }
void mesh_bed_leveling::reset() {
active = 0;
for (int y = 0; y < MESH_NUM_Y_POINTS; y++)
for (int x = 0; x < MESH_NUM_X_POINTS; x++)
z_values[y][x] = 0;
}
static inline bool vec_undef(const float v[2])
{
const uint32_t *vx = (const uint32_t*)v;
return vx[0] == 0x0FFFFFFFF || vx[1] == 0x0FFFFFFFF;
}
#if MESH_NUM_X_POINTS>=5 && MESH_NUM_Y_POINTS>=5 && (MESH_NUM_X_POINTS&1)==1 && (MESH_NUM_Y_POINTS&1)==1
// Works for an odd number of MESH_NUM_X_POINTS and MESH_NUM_Y_POINTS
// #define MBL_BILINEAR
void mesh_bed_leveling::upsample_3x3()
{
int idx0 = 0;
int idx1 = MESH_NUM_X_POINTS / 2;
int idx2 = MESH_NUM_X_POINTS - 1;
{
// First interpolate the points in X axis.
static const float x0 = MESH_MIN_X;
static const float x1 = 0.5f * float(MESH_MIN_X + MESH_MAX_X);
static const float x2 = MESH_MAX_X;
for (int j = 0; j < 3; ++ j) {
// 1) Copy the source points to their new destination.
z_values[j][idx2] = z_values[j][2];
z_values[j][idx1] = z_values[j][1];
// 2) Interpolate the remaining values by Largrangian polynomials.
for (int i = idx0 + 1; i < idx2; ++ i) {
if (i == idx1)
continue;
float x = get_x(i);
#ifdef MBL_BILINEAR
z_values[j][i] = (x < x1) ?
((z_values[j][idx0] * (x - x0) + z_values[j][idx1] * (x1 - x)) / (x1 - x0)) :
((z_values[j][idx1] * (x - x1) + z_values[j][idx2] * (x2 - x)) / (x2 - x1));
#else
z_values[j][i] =
z_values[j][idx0] * (x - x1) * (x - x2) / ((x0 - x1) * (x0 - x2)) +
z_values[j][idx1] * (x - x0) * (x - x2) / ((x1 - x0) * (x1 - x2)) +
z_values[j][idx2] * (x - x0) * (x - x1) / ((x2 - x0) * (x2 - x1));
#endif
}
}
}
{
// Second interpolate the points in Y axis.
static const float y0 = MESH_MIN_Y;
static const float y1 = 0.5f * float(MESH_MIN_Y + MESH_MAX_Y);
static const float y2 = MESH_MAX_Y;
for (int i = 0; i < MESH_NUM_X_POINTS; ++ i) {
// 1) Copy the intermediate points to their new destination.
z_values[idx2][i] = z_values[2][i];
z_values[idx1][i] = z_values[1][i];
// 2) Interpolate the remaining values by Largrangian polynomials.
for (int j = 1; j + 1 < MESH_NUM_Y_POINTS; ++ j) {
if (j == idx1)
continue;
float y = get_y(j);
#ifdef MBL_BILINEAR
z_values[j][i] = (y < y1) ?
((z_values[idx0][i] * (y - y0) + z_values[idx1][i] * (y1 - y)) / (y1 - y0)) :
((z_values[idx1][i] * (y - y1) + z_values[idx2][i] * (y2 - y)) / (y2 - y1));
#else
z_values[j][i] =
z_values[idx0][i] * (y - y1) * (y - y2) / ((y0 - y1) * (y0 - y2)) +
z_values[idx1][i] * (y - y0) * (y - y2) / ((y1 - y0) * (y1 - y2)) +
z_values[idx2][i] * (y - y0) * (y - y1) / ((y2 - y0) * (y2 - y1));
#endif
}
}
}
/*
// Relax the non-measured points.
const float weight = 0.2f;
for (uint8_t iter = 0; iter < 20; ++ iter) {
for (int8_t j = 1; j < 6; ++ j) {
for (int8_t i = 1; i < 6; ++ i) {
if (i == 3 || j == 3)
continue;
if ((i % 3) == 0 && (j % 3) == 0)
continue;
float avg = 0.25f * (z_values[j][i-1]+z_values[j][i+1]+z_values[j-1][i]+z_values[j+1][i]);
z_values[j][i] = (1.f-weight)*z_values[j][i] + weight*avg;
}
}
}
*/
}
#endif
#endif // MESH_BED_LEVELING

View File

@ -0,0 +1,125 @@
#include "Marlin.h"
#ifdef MESH_BED_LEVELING
#define MEAS_NUM_X_DIST (float(MESH_MAX_X - MESH_MIN_X)/float(MESH_MEAS_NUM_X_POINTS - 1))
#define MEAS_NUM_Y_DIST (float(MESH_MAX_Y - MESH_MIN_Y)/float(MESH_MEAS_NUM_Y_POINTS - 1))
#define MESH_X_DIST (float(MESH_MAX_X - MESH_MIN_X)/float(MESH_NUM_X_POINTS - 1))
#define MESH_Y_DIST (float(MESH_MAX_Y - MESH_MIN_Y)/float(MESH_NUM_Y_POINTS - 1))
class mesh_bed_leveling {
public:
uint8_t active;
float z_values[MESH_NUM_Y_POINTS][MESH_NUM_X_POINTS];
mesh_bed_leveling();
void reset();
#if MESH_NUM_X_POINTS>=5 && MESH_NUM_Y_POINTS>=5 && (MESH_NUM_X_POINTS&1)==1 && (MESH_NUM_Y_POINTS&1)==1
void upsample_3x3();
#endif
static float get_x(int i) { return float(MESH_MIN_X) + float(MESH_X_DIST) * float(i); }
static float get_y(int i) { return float(MESH_MIN_Y) + float(MESH_Y_DIST) * float(i); }
// Measurement point for the Z probe.
// If use_default=true, then the default positions for a correctly built printer are used.
// Otherwise a correction matrix is pulled from the EEPROM if available.
static void get_meas_xy(int ix, int iy, float &x, float &y, bool use_default);
void set_z(int ix, int iy, float z) { z_values[iy][ix] = z; }
int select_x_index(float x) {
int i = 1;
while (x > get_x(i) && i < MESH_NUM_X_POINTS - 1) i++;
return i - 1;
}
int select_y_index(float y) {
int i = 1;
while (y > get_y(i) && i < MESH_NUM_Y_POINTS - 1) i++;
return i - 1;
}
float get_z(float x, float y) {
int i, j;
float s, t;
#if MESH_NUM_X_POINTS==3 && MESH_NUM_Y_POINTS==3
#define MESH_MID_X (0.5f*(MESH_MIN_X+MESH_MAX_X))
#define MESH_MID_Y (0.5f*(MESH_MIN_Y+MESH_MAX_Y))
if (x < MESH_MID_X) {
i = 0;
s = (x - MESH_MIN_X) / MESH_X_DIST;
if (s > 1.f)
s = 1.f;
} else {
i = 1;
s = (x - MESH_MID_X) / MESH_X_DIST;
if (s < 0)
s = 0;
}
if (y < MESH_MID_Y) {
j = 0;
t = (y - MESH_MIN_Y) / MESH_Y_DIST;
if (t > 1.f)
t = 1.f;
} else {
j = 1;
t = (y - MESH_MID_Y) / MESH_Y_DIST;
if (t < 0)
t = 0;
}
#else
i = int(floor((x - MESH_MIN_X) / MESH_X_DIST));
if (i < 0) {
i = 0;
s = (x - MESH_MIN_X) / MESH_X_DIST;
if (s > 1.f)
s = 1.f;
}
else if (i > MESH_NUM_X_POINTS - 2) {
i = MESH_NUM_X_POINTS - 2;
s = (x - get_x(i)) / MESH_X_DIST;
if (s < 0)
s = 0;
} else {
s = (x - get_x(i)) / MESH_X_DIST;
if (s < 0)
s = 0;
else if (s > 1.f)
s = 1.f;
}
j = int(floor((y - MESH_MIN_Y) / MESH_Y_DIST));
if (j < 0) {
j = 0;
t = (y - MESH_MIN_Y) / MESH_Y_DIST;
if (t > 1.f)
t = 1.f;
} else if (j > MESH_NUM_Y_POINTS - 2) {
j = MESH_NUM_Y_POINTS - 2;
t = (y - get_y(j)) / MESH_Y_DIST;
if (t < 0)
t = 0;
} else {
t = (y - get_y(j)) / MESH_Y_DIST;
if (t < 0)
t = 0;
else if (t > 1.f)
t = 1.f;
}
#endif /* MESH_NUM_X_POINTS==3 && MESH_NUM_Y_POINTS==3 */
float si = 1.f-s;
float z0 = si * z_values[j ][i] + s * z_values[j ][i+1];
float z1 = si * z_values[j+1][i] + s * z_values[j+1][i+1];
return (1.f-t) * z0 + t * z1;
}
};
extern mesh_bed_leveling mbl;
#endif // MESH_BED_LEVELING

136
Firmware/messages.c Normal file
View File

@ -0,0 +1,136 @@
//messages.c
#include "language.h"
//this is because we need include Configuration_prusa.h (CUSTOM_MENDEL_NAME)
#define bool char
#define true 1
#define false 0
#include "Configuration_prusa.h"
//internationalized messages
const char MSG_AUTO_HOME[] PROGMEM_I1 = ISTR("Auto home"); ////
const char MSG_AUTO_MODE_ON[] PROGMEM_I1 = ISTR("Mode [auto power]"); ////
const char MSG_BABYSTEP_Z[] PROGMEM_I1 = ISTR("Live adjust Z"); //// c=18
const char MSG_BABYSTEP_Z_NOT_SET[] PROGMEM_I1 = ISTR("Distance between tip of the nozzle and the bed surface has not been set yet. Please follow the manual, chapter First steps, section First layer calibration."); ////c=20 r=12
const char MSG_BED[] PROGMEM_I1 = ISTR("Bed"); ////
const char MSG_BED_DONE[] PROGMEM_I1 = ISTR("Bed done"); ////
const char MSG_BED_HEATING[] PROGMEM_I1 = ISTR("Bed Heating"); ////
const char MSG_BED_LEVELING_FAILED_POINT_LOW[] PROGMEM_I1 = ISTR("Bed leveling failed. Sensor didnt trigger. Debris on nozzle? Waiting for reset."); ////c=20 r=4
const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED[] PROGMEM_I1 = ISTR("XYZ calibration failed. Please consult the manual."); ////c=20 r=8
const char MSG_CALIBRATE_Z_AUTO[] PROGMEM_I1 = ISTR("Calibrating Z"); ////c=20 r=2
const char MSG_CARD_MENU[] PROGMEM_I1 = ISTR("Print from SD"); ////
const char MSG_CONFIRM_NOZZLE_CLEAN[] PROGMEM_I1 = ISTR("Please clean the nozzle for calibration. Click when done."); ////c=20 r=8
const char MSG_COOLDOWN[] PROGMEM_I1 = ISTR("Cooldown"); ////
const char MSG_CRASH_DETECTED[] PROGMEM_I1 = ISTR("Crash detected."); ////c=20 r=1
const char MSG_CRASHDETECT_NA[] PROGMEM_I1 = ISTR("Crash det. [N/A]"); ////
const char MSG_CRASHDETECT_OFF[] PROGMEM_I1 = ISTR("Crash det. [off]"); ////
const char MSG_CRASHDETECT_ON[] PROGMEM_I1 = ISTR("Crash det. [on]"); ////
const char MSG_ERROR[] PROGMEM_I1 = ISTR("ERROR:"); ////
const char MSG_EXTRUDER[] PROGMEM_I1 = ISTR("Extruder"); ////c=17 r=1
const char MSG_FILAMENT[] PROGMEM_I1 = ISTR("Filament"); ////c=17 r=1
const char MSG_FAN_SPEED[] PROGMEM_I1 = ISTR("Fan speed"); ////c=14
const char MSG_FILAMENT_CLEAN[] PROGMEM_I1 = ISTR("Filament extruding & with correct color?"); ////c=20 r=2
const char MSG_FILAMENT_LOADING_T0[] PROGMEM_I1 = ISTR("Insert filament into extruder 1. Click when done."); ////c=20 r=4
const char MSG_FILAMENT_LOADING_T1[] PROGMEM_I1 = ISTR("Insert filament into extruder 2. Click when done."); ////c=20 r=4
const char MSG_FILAMENT_LOADING_T2[] PROGMEM_I1 = ISTR("Insert filament into extruder 3. Click when done."); ////c=20 r=4
const char MSG_FILAMENT_LOADING_T3[] PROGMEM_I1 = ISTR("Insert filament into extruder 4. Click when done."); ////c=20 r=4
const char MSG_FILAMENTCHANGE[] PROGMEM_I1 = ISTR("Change filament"); ////
const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE1[] PROGMEM_I1 = ISTR("Searching bed calibration point"); ////c=60
const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE2[] PROGMEM_I1 = ISTR(" of 4"); ////c=14
const char MSG_FINISHING_MOVEMENTS[] PROGMEM_I1 = ISTR("Finishing movements"); ////c=20 r=1
const char MSG_FOLLOW_CALIBRATION_FLOW[] PROGMEM_I1 = ISTR("Printer has not been calibrated yet. Please follow the manual, chapter First steps, section Calibration flow."); ////c=20 r=8
const char MSG_FOLLOW_Z_CALIBRATION_FLOW[] PROGMEM_I1 = ISTR("There is still a need to make Z calibration. Please follow the manual, chapter First steps, section Calibration flow."); ////c=20 r=8
const char MSG_FSENS_AUTOLOAD_NA[] PROGMEM_I1 = ISTR("F. autoload [N/A]"); ////c=17 r=1
const char MSG_FSENSOR_OFF[] PROGMEM_I1 = ISTR("Fil. sensor [off]"); ////
const char MSG_FSENSOR_ON[] PROGMEM_I1 = ISTR("Fil. sensor [on]"); ////
const char MSG_HEATING[] PROGMEM_I1 = ISTR("Heating"); ////
const char MSG_HEATING_COMPLETE[] PROGMEM_I1 = ISTR("Heating done."); ////c=20
const char MSG_HOMEYZ[] PROGMEM_I1 = ISTR("Calibrate Z"); ////
const char MSG_CHOOSE_EXTRUDER[] PROGMEM_I1 = ISTR("Choose extruder:"); ////c=20 r=1
const char MSG_CHOOSE_FILAMENT[] PROGMEM_I1 = ISTR("Choose filament:"); ////c=20 r=1
const char MSG_LOAD_FILAMENT[] PROGMEM_I1 = ISTR("Load filament"); //// Number 1 to 5 is added behind text e.g. "Load filament 1" c=16
const char MSG_LOADING_FILAMENT[] PROGMEM_I1 = ISTR("Loading filament"); ////c=20
const char MSG_EJECT_FILAMENT[] PROGMEM_I1 = ISTR("Eject filament"); //// Number 1 to 5 is added behind text e.g. "Eject filament 1" c=16
const char MSG_CUT_FILAMENT[] PROGMEM_I1 = ISTR("Cut filament"); //// Number 1 to 5 is added behind text e.g. "Cut filament 1" c=16
const char MSG_M117_V2_CALIBRATION[] PROGMEM_I1 = ISTR("M117 First layer cal."); ////c=25 r=1
const char MSG_MAIN[] PROGMEM_I1 = ISTR("Main"); ////
const char MSG_BACK[] PROGMEM_I1 = ISTR("Back"); ////
const char MSG_SHEET[] PROGMEM_I1 = ISTR("Sheet"); ////c=10
const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1[] PROGMEM_I1 = ISTR("Measuring reference height of calibration point"); ////c=60
const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE2[] PROGMEM_I1 = ISTR(" of 9"); ////c=14
const char MSG_MENU_CALIBRATION[] PROGMEM_I1 = ISTR("Calibration"); ////
const char MSG_NO[] PROGMEM_I1 = ISTR("No"); ////
const char MSG_NOZZLE[] PROGMEM_I1 = ISTR("Nozzle"); ////
const char MSG_PAPER[] PROGMEM_I1 = ISTR("Place a sheet of paper under the nozzle during the calibration of first 4 points. If the nozzle catches the paper, power off the printer immediately."); ////c=20 r=8
const char MSG_PLACE_STEEL_SHEET[] PROGMEM_I1 = ISTR("Please place steel sheet on heatbed."); ////c=20 r=4
const char MSG_PLEASE_WAIT[] PROGMEM_I1 = ISTR("Please wait"); ////c=20
const char MSG_PREHEAT_NOZZLE[] PROGMEM_I1 = ISTR("Preheat the nozzle!"); ////c=20
const char MSG_PRESS_TO_UNLOAD[] PROGMEM_I1 = ISTR("Please press the knob to unload filament"); ////c=20 r=4
const char MSG_PRINT_ABORTED[] PROGMEM_I1 = ISTR("Print aborted"); ////c=20
const char MSG_PULL_OUT_FILAMENT[] PROGMEM_I1 = ISTR("Please pull out filament immediately"); ////c=20 r=4
const char MSG_RECOVER_PRINT[] PROGMEM_I1 = ISTR("Blackout occurred. Recover print?"); ////c=20 r=2
const char MSG_REFRESH[] PROGMEM_I1 = ISTR("\xF8" "Refresh"); ////
const char MSG_RESUMING_PRINT[] PROGMEM_I1 = ISTR("Resuming print"); ////
const char MSG_REMOVE_STEEL_SHEET[] PROGMEM_I1 = ISTR("Please remove steel sheet from heatbed."); ////c=20 r=4
const char MSG_SELFTEST_COOLING_FAN[] PROGMEM_I1 = ISTR("Front print fan?"); ////c=20
const char MSG_SELFTEST_EXTRUDER_FAN[] PROGMEM_I1 = ISTR("Left hotend fan?"); ////c=20
const char MSG_SELFTEST_FAILED[] PROGMEM_I1 = ISTR("Selftest failed "); ////c=20
const char MSG_SELFTEST_FAN[] PROGMEM_I1 = ISTR("Fan test"); ////c=20
const char MSG_SELFTEST_FAN_NO[] PROGMEM_I1 = ISTR("Not spinning"); ////c=19
const char MSG_SELFTEST_FAN_YES[] PROGMEM_I1 = ISTR("Spinning"); ////c=19
const char MSG_SELFTEST_CHECK_BED[] PROGMEM_I1 = ISTR("Checking bed "); ////c=20
const char MSG_SELFTEST_CHECK_FSENSOR[] PROGMEM_I1 = ISTR("Checking sensors "); ////c=20
const char MSG_SELFTEST_MOTOR[] PROGMEM_I1 = ISTR("Motor"); ////
const char MSG_SELFTEST_FILAMENT_SENSOR[] PROGMEM_I1 = ISTR("Filament sensor"); ////c=17
const char MSG_SELFTEST_WIRINGERROR[] PROGMEM_I1 = ISTR("Wiring error"); ////
const char MSG_SETTINGS[] PROGMEM_I1 = ISTR("Settings"); ////
const char MSG_HW_SETUP[] PROGMEM_I1 = ISTR("HW Setup"); ////
const char MSG_SILENT_MODE_OFF[] PROGMEM_I1 = ISTR("Mode [high power]"); ////
const char MSG_SILENT_MODE_ON[] PROGMEM_I1 = ISTR("Mode [silent]"); ////
const char MSG_STEALTH_MODE_OFF[] PROGMEM_I1 = ISTR("Mode [Normal]"); ////
const char MSG_STEALTH_MODE_ON[] PROGMEM_I1 = ISTR("Mode [Stealth]"); ////
const char MSG_STEEL_SHEET_CHECK[] PROGMEM_I1 = ISTR("Is steel sheet on heatbed?"); ////c=20 r=2
const char MSG_STOP_PRINT[] PROGMEM_I1 = ISTR("Stop print"); ////
const char MSG_STOPPED[] PROGMEM_I1 = ISTR("STOPPED. "); ////
const char MSG_TEMP_CALIBRATION[] PROGMEM_I1 = ISTR("Temp. cal. "); ////c=20 r=1
const char MSG_TEMP_CALIBRATION_DONE[] PROGMEM_I1 = ISTR("Temperature calibration is finished and active. Temp. calibration can be disabled in menu Settings->Temp. cal."); ////c=20 r=12
const char MSG_UNLOAD_FILAMENT[] PROGMEM_I1 = ISTR("Unload filament"); ////c=17
const char MSG_UNLOADING_FILAMENT[] PROGMEM_I1 = ISTR("Unloading filament"); ////c=20 r=1
const char MSG_WATCH[] PROGMEM_I1 = ISTR("Info screen"); ////
const char MSG_WIZARD_CALIBRATION_FAILED[] PROGMEM_I1 = ISTR("Please check our handbook and fix the problem. Then resume the Wizard by rebooting the printer."); ////c=20 r=8
const char MSG_WIZARD_DONE[] PROGMEM_I1 = ISTR("All is done. Happy printing!"); ////c=20 r=8
const char MSG_WIZARD_HEATING[] PROGMEM_I1 = ISTR("Preheating nozzle. Please wait."); ////c=20 r=3
const char MSG_WIZARD_QUIT[] PROGMEM_I1 = ISTR("You can always resume the Wizard from Calibration -> Wizard."); ////c=20 r=8
const char MSG_YES[] PROGMEM_I1 = ISTR("Yes"); ////
const char MSG_V2_CALIBRATION[] PROGMEM_I1 = ISTR("First layer cal."); ////c=17 r=1
const char WELCOME_MSG[] PROGMEM_I1 = ISTR(CUSTOM_MENDEL_NAME " OK."); ////c=20
//not internationalized messages
const char MSG_SD_WORKDIR_FAIL[] PROGMEM_N1 = "workDir open failed"; ////
const char MSG_BROWNOUT_RESET[] PROGMEM_N1 = " Brown out Reset"; ////
const char MSG_EXTERNAL_RESET[] PROGMEM_N1 = " External Reset"; ////
const char MSG_FILE_SAVED[] PROGMEM_N1 = "Done saving file."; ////
const char MSG_OFF[] PROGMEM_N1 = "Off"; ////
const char MSG_ON[] PROGMEM_N1 = "On "; ////
const char MSG_POSITION_UNKNOWN[] PROGMEM_N1 = "Home X/Y before Z"; ////
const char MSG_SOFTWARE_RESET[] PROGMEM_N1 = " Software Reset"; ////
const char MSG_UNKNOWN_COMMAND[] PROGMEM_N1 = "Unknown command: \""; ////
const char MSG_WATCHDOG_RESET[] PROGMEM_N1 = " Watchdog Reset"; ////
const char MSG_Z_MAX[] PROGMEM_N1 = "z_max: "; ////
const char MSG_Z_MIN[] PROGMEM_N1 = "z_min: "; ////
const char MSG_ZPROBE_OUT[] PROGMEM_N1 = "Z probe out. bed"; ////
const char MSG_ZPROBE_ZOFFSET[] PROGMEM_N1 = "Z Offset"; ////
const char MSG_TMC_OVERTEMP[] PROGMEM_N1 = "TMC DRIVER OVERTEMP"; ////
const char MSG_Enqueing[] PROGMEM_N1 = "enqueing \""; ////
const char MSG_ENDSTOPS_HIT[] PROGMEM_N1 = "endstops hit: "; ////
const char MSG_SD_ERR_WRITE_TO_FILE[] PROGMEM_N1 = "error writing to file"; ////
const char MSG_OK[] PROGMEM_N1 = "ok"; ////
const char MSG_SD_OPEN_FILE_FAIL[] PROGMEM_N1 = "open failed, File: "; ////
const char MSG_ENDSTOP_OPEN[] PROGMEM_N1 = "open"; ////
const char MSG_POWERUP[] PROGMEM_N1 = "PowerUp"; ////
const char MSG_ERR_STOPPED[] PROGMEM_N1 = "Printer stopped due to errors. Fix the error and use M999 to restart. (Temperature is reset. Set it after restarting)"; ////
const char MSG_ENDSTOP_HIT[] PROGMEM_N1 = "TRIGGERED"; ////
const char MSG_OCTOPRINT_PAUSED[] PROGMEM_N1 = "// action:paused"; ////
const char MSG_OCTOPRINT_RESUMED[] PROGMEM_N1 = "// action:resumed"; ////
const char MSG_OCTOPRINT_CANCEL[] PROGMEM_N1 = "// action:cancel"; ////
const char MSG_FANCHECK_EXTRUDER[] PROGMEM_N1 = "Err: EXTR. FAN ERROR"; ////c=20
const char MSG_FANCHECK_PRINT[] PROGMEM_N1 = "Err: PRINT FAN ERROR"; ////c=20

141
Firmware/messages.h Normal file
View File

@ -0,0 +1,141 @@
//messages.h
// Common serial messages
#define MSG_MARLIN "Marlin"
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
// LCD Menu Messages
//internationalized messages
extern const char MSG_AUTO_HOME[];
extern const char MSG_AUTO_MODE_ON[];
extern const char MSG_BABYSTEP_Z[];
extern const char MSG_BABYSTEP_Z_NOT_SET[];
extern const char MSG_BED[];
extern const char MSG_BED_DONE[];
extern const char MSG_BED_HEATING[];
extern const char MSG_BED_LEVELING_FAILED_POINT_LOW[];
extern const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED[];
extern const char MSG_CALIBRATE_Z_AUTO[];
extern const char MSG_CARD_MENU[];
extern const char MSG_CONFIRM_NOZZLE_CLEAN[];
extern const char MSG_COOLDOWN[];
extern const char MSG_CRASH_DETECTED[];
extern const char MSG_CRASHDETECT_NA[];
extern const char MSG_CRASHDETECT_OFF[];
extern const char MSG_CRASHDETECT_ON[];
extern const char MSG_ERROR[];
extern const char MSG_EXTRUDER[];
extern const char MSG_FILAMENT[];
extern const char MSG_FAN_SPEED[];
extern const char MSG_FILAMENT_CLEAN[];
extern const char MSG_FILAMENT_LOADING_T0[];
extern const char MSG_FILAMENT_LOADING_T1[];
extern const char MSG_FILAMENT_LOADING_T2[];
extern const char MSG_FILAMENT_LOADING_T3[];
extern const char MSG_FILAMENTCHANGE[];
extern const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE1[];
extern const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE2[];
extern const char MSG_FINISHING_MOVEMENTS[];
extern const char MSG_FOLLOW_CALIBRATION_FLOW[];
extern const char MSG_FOLLOW_Z_CALIBRATION_FLOW[];
extern const char MSG_FSENS_AUTOLOAD_NA[];
extern const char MSG_FSENSOR_OFF[];
extern const char MSG_FSENSOR_ON[];
extern const char MSG_HEATING[];
extern const char MSG_HEATING_COMPLETE[];
extern const char MSG_HOMEYZ[];
extern const char MSG_CHOOSE_EXTRUDER[];
extern const char MSG_CHOOSE_FILAMENT[];
extern const char MSG_LOAD_FILAMENT[];
extern const char MSG_LOADING_FILAMENT[];
extern const char MSG_M117_V2_CALIBRATION[];
extern const char MSG_MAIN[];
extern const char MSG_BACK[];
extern const char MSG_SHEET[];
extern const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1[];
extern const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE2[];
extern const char MSG_MENU_CALIBRATION[];
extern const char MSG_NO[];
extern const char MSG_NOZZLE[];
extern const char MSG_PAPER[];
extern const char MSG_PLACE_STEEL_SHEET[];
extern const char MSG_PLEASE_WAIT[];
extern const char MSG_PREHEAT_NOZZLE[];
extern const char MSG_PRESS_TO_UNLOAD[];
extern const char MSG_PRINT_ABORTED[];
extern const char MSG_PULL_OUT_FILAMENT[];
extern const char MSG_RECOVER_PRINT[];
extern const char MSG_REFRESH[];
extern const char MSG_REMOVE_STEEL_SHEET[];
extern const char MSG_RESUMING_PRINT[];
extern const char MSG_SD_WORKDIR_FAIL[];
extern const char MSG_SELFTEST_COOLING_FAN[];
extern const char MSG_SELFTEST_EXTRUDER_FAN[];
extern const char MSG_SELFTEST_FAILED[];
extern const char MSG_SELFTEST_FAN[];
extern const char MSG_SELFTEST_FAN_NO[];
extern const char MSG_SELFTEST_FAN_YES[];
extern const char MSG_SELFTEST_CHECK_BED[];
extern const char MSG_SELFTEST_CHECK_FSENSOR[];
extern const char MSG_SELFTEST_MOTOR[];
extern const char MSG_SELFTEST_FILAMENT_SENSOR[];
extern const char MSG_SELFTEST_WIRINGERROR[];
extern const char MSG_SETTINGS[];
extern const char MSG_HW_SETUP[];
extern const char MSG_SILENT_MODE_OFF[];
extern const char MSG_SILENT_MODE_ON[];
extern const char MSG_STEALTH_MODE_OFF[];
extern const char MSG_STEALTH_MODE_ON[];
extern const char MSG_STEEL_SHEET_CHECK[];
extern const char MSG_STOP_PRINT[];
extern const char MSG_STOPPED[];
extern const char MSG_TEMP_CALIBRATION[];
extern const char MSG_TEMP_CALIBRATION_DONE[];
extern const char MSG_UNLOAD_FILAMENT[];
extern const char MSG_UNLOADING_FILAMENT[];
extern const char MSG_WATCH[];
extern const char MSG_WIZARD_CALIBRATION_FAILED[];
extern const char MSG_WIZARD_DONE[];
extern const char MSG_WIZARD_HEATING[];
extern const char MSG_WIZARD_QUIT[];
extern const char MSG_YES[];
extern const char MSG_V2_CALIBRATION[];
extern const char WELCOME_MSG[];
//not internationalized messages
extern const char MSG_BROWNOUT_RESET[];
extern const char MSG_EXTERNAL_RESET[];
extern const char MSG_FILE_SAVED[];
extern const char MSG_OFF[];
extern const char MSG_ON[];
extern const char MSG_POSITION_UNKNOWN[];
extern const char MSG_SOFTWARE_RESET[];
extern const char MSG_UNKNOWN_COMMAND[];
extern const char MSG_WATCHDOG_RESET[];
extern const char MSG_Z_MAX[];
extern const char MSG_Z_MIN[];
extern const char MSG_ZPROBE_OUT[];
extern const char MSG_ZPROBE_ZOFFSET[];
extern const char MSG_TMC_OVERTEMP[];
extern const char MSG_Enqueing[];
extern const char MSG_ENDSTOPS_HIT[];
extern const char MSG_SD_ERR_WRITE_TO_FILE[];
extern const char MSG_OK[];
extern const char MSG_SD_OPEN_FILE_FAIL[];
extern const char MSG_ENDSTOP_OPEN[];
extern const char MSG_POWERUP[];
extern const char MSG_ERR_STOPPED[];
extern const char MSG_ENDSTOP_HIT[];
extern const char MSG_EJECT_FILAMENT[];
extern const char MSG_CUT_FILAMENT[];
extern const char MSG_OCTOPRINT_PAUSED[];
extern const char MSG_OCTOPRINT_RESUMED[];
extern const char MSG_OCTOPRINT_CANCEL[];
extern const char MSG_FANCHECK_EXTRUDER[];
extern const char MSG_FANCHECK_PRINT[];
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)

1610
Firmware/mmu.cpp Normal file

File diff suppressed because it is too large Load Diff

141
Firmware/mmu.h Normal file
View File

@ -0,0 +1,141 @@
//! @file
#ifndef MMU_H
#define MMU_H
#include <inttypes.h>
extern bool mmu_enabled;
extern bool mmu_fil_loaded;
extern uint8_t mmu_extruder;
extern uint8_t tmp_extruder;
extern int8_t mmu_finda;
extern bool ir_sensor_detected;
extern int16_t mmu_version;
extern int16_t mmu_buildnr;
extern uint16_t mmu_power_failures;
#define MMU_FILAMENT_UNKNOWN 255
#define MMU_NO_MOVE 0
#define MMU_UNLOAD_MOVE 1
#define MMU_LOAD_MOVE 2
#define MMU_TCODE_MOVE 3
#define MMU_LOAD_FEEDRATE 19.02f //mm/s
#define MMU_LOAD_TIME_MS 2000 //should be fine tuned to load time for shortest allowed PTFE tubing and maximum loading speed
enum class MmuCmd : uint_least8_t
{
None,
T0,
T1,
T2,
T3,
T4,
L0,
L1,
L2,
L3,
L4,
C0,
U0,
E0,
E1,
E2,
E3,
E4,
K0,
K1,
K2,
K3,
K4,
R0,
S3,
W0, //!< Wait and signal load error
};
inline MmuCmd operator+ (MmuCmd cmd, uint8_t filament)
{
return static_cast<MmuCmd>(static_cast<uint8_t>(cmd) + filament );
}
inline uint8_t operator- (MmuCmd cmda, MmuCmd cmdb)
{
return (static_cast<uint8_t>(cmda) - static_cast<uint8_t>(cmdb));
}
extern int mmu_puts_P(const char* str);
extern int mmu_printf_P(const char* format, ...);
extern int8_t mmu_rx_ok(void);
extern bool check_for_ir_sensor();
extern void mmu_init(void);
extern void mmu_loop(void);
extern void mmu_reset(void);
extern int8_t mmu_set_filament_type(uint8_t extruder, uint8_t filament);
extern void mmu_command(MmuCmd cmd);
extern bool mmu_get_response(uint8_t move = 0);
extern void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move = MMU_NO_MOVE);
extern void mmu_load_to_nozzle();
extern void mmu_M600_load_filament(bool automatic, float nozzle_temp);
extern void mmu_M600_wait_and_beep();
extern void extr_mov(float shift, float feed_rate);
extern void change_extr(int extr);
extern int get_ext_nr();
extern void display_loading();
extern void extr_adj(uint8_t extruder);
extern void extr_unload();
extern void extr_adj_0();
extern void extr_adj_1();
extern void extr_adj_2();
extern void extr_adj_3();
extern void extr_adj_4();
extern void load_all();
extern void extr_change_0();
extern void extr_change_1();
extern void extr_change_2();
extern void extr_change_3();
#ifdef SNMM
extern void extr_unload_all();
extern void extr_unload_used();
#endif //SNMM
extern void extr_unload_0();
extern void extr_unload_1();
extern void extr_unload_2();
extern void extr_unload_3();
extern void extr_unload_4();
extern bool mmu_check_version();
extern void mmu_show_warning();
extern void lcd_mmu_load_to_nozzle(uint8_t filament_nr);
extern void mmu_eject_filament(uint8_t filament, bool recover);
#ifdef MMU_HAS_CUTTER
extern void mmu_cut_filament(uint8_t filament_nr);
#endif //MMU_HAS_CUTTER
extern void mmu_continue_loading(bool blocking);
extern void mmu_filament_ramming();
extern void mmu_wait_for_heater_blocking();
extern void mmu_load_step(bool synchronize = true);
#endif //MMU_H

145
Firmware/motion_control.cpp Normal file
View File

@ -0,0 +1,145 @@
/*
motion_control.c - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Grbl 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.
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#include "stepper.h"
#include "planner.h"
// 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, uint8_t axis_0, uint8_t axis_1,
uint8_t axis_linear, float feed_rate, float radius, uint8_t isclockwise, uint8_t extruder)
{
// 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[axis_0] + offset[axis_0];
float center_axis1 = position[axis_1] + offset[axis_1];
float linear_travel = target[axis_linear] - position[axis_linear];
float extruder_travel = target[E_AXIS] - position[E_AXIS];
float r_axis0 = -offset[axis_0]; // Radius vector from center to current location
float r_axis1 = -offset[axis_1];
float rt_axis0 = target[axis_0] - center_axis0;
float rt_axis1 = target[axis_1] - center_axis1;
// 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; }
//20141002:full circle for G03 did not work, e.g. G03 X80 Y80 I20 J0 F2000 is giving an Angle of zero so head is not moving
//to compensate when start pos = target pos && angle is zero -> angle = 2Pi
if (position[axis_0] == target[axis_0] && position[axis_1] == target[axis_1] && angular_travel == 0)
{
angular_travel += 2*M_PI;
}
//end fix G03
float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
if (millimeters_of_travel < 0.001) { return; }
uint16_t segments = 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[E_AXIS] = position[E_AXIS];
for (i = 1; i<segments; i++) { // Increment (segments-1)
if (count < N_ARC_CORRECTION) {
// Apply vector rotation matrix
r_axisi = r_axis0*sin_T + r_axis1*cos_T;
r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
r_axis1 = r_axisi;
count++;
} else {
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
cos_Ti = cos(i*theta_per_segment);
sin_Ti = sin(i*theta_per_segment);
r_axis0 = -offset[axis_0]*cos_Ti + offset[axis_1]*sin_Ti;
r_axis1 = -offset[axis_0]*sin_Ti - offset[axis_1]*cos_Ti;
count = 0;
}
// Update arc_target location
arc_target[axis_0] = center_axis0 + r_axis0;
arc_target[axis_1] = center_axis1 + r_axis1;
arc_target[axis_linear] += linear_per_segment;
arc_target[E_AXIS] += extruder_per_segment;
clamp_to_software_endstops(arc_target);
plan_buffer_line(arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], arc_target[E_AXIS], feed_rate, extruder);
}
// Ensure last segment arrives at target location.
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feed_rate, extruder);
// plan_set_acceleration_manager_enabled(acceleration_manager_was_enabled);
}

32
Firmware/motion_control.h Normal file
View File

@ -0,0 +1,32 @@
/*
motion_control.h - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Grbl 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.
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef motion_control_h
#define motion_control_h
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
// for vector transformation direction.
void mc_arc(float *position, float *target, float *offset, unsigned char axis_0, unsigned char axis_1,
unsigned char axis_linear, float feed_rate, float radius, unsigned char isclockwise, uint8_t extruder);
#endif

View File

@ -0,0 +1,311 @@
//! @file
// Based on the OptiBoot project
// https://github.com/Optiboot/optiboot
// Licence GLP 2 or later.
#include "Marlin.h"
#include "w25x20cl.h"
#include "stk500.h"
#include "bootapp.h"
#define OPTIBOOT_MAJVER 6
#define OPTIBOOT_CUSTOMVER 0
#define OPTIBOOT_MINVER 2
static unsigned const int __attribute__((section(".version")))
optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
/* Watchdog settings */
#define WATCHDOG_OFF (0)
#define WATCHDOG_16MS (_BV(WDE))
#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
#if 0
#define W25X20CL_SIGNATURE_0 9
#define W25X20CL_SIGNATURE_1 8
#define W25X20CL_SIGNATURE_2 7
#else
//FIXME this is a signature of ATmega2560!
#define W25X20CL_SIGNATURE_0 0x1E
#define W25X20CL_SIGNATURE_1 0x98
#define W25X20CL_SIGNATURE_2 0x01
#endif
static void watchdogConfig(uint8_t x) {
WDTCSR = _BV(WDCE) | _BV(WDE);
WDTCSR = x;
}
static void watchdogReset() {
__asm__ __volatile__ (
"wdr\n"
);
}
#define RECV_READY ((UCSR0A & _BV(RXC0)) != 0)
static uint8_t getch(void) {
uint8_t ch;
while(! RECV_READY) ;
if (!(UCSR0A & _BV(FE0))) {
/*
* A Framing Error indicates (probably) that something is talking
* to us at the wrong bit rate. Assume that this is because it
* expects to be talking to the application, and DON'T reset the
* watchdog. This should cause the bootloader to abort and run
* the application "soon", if it keeps happening. (Note that we
* don't care that an invalid char is returned...)
*/
watchdogReset();
}
ch = UDR0;
return ch;
}
static void putch(char ch) {
while (!(UCSR0A & _BV(UDRE0)));
UDR0 = ch;
}
static void verifySpace() {
if (getch() != CRC_EOP) {
putch(STK_FAILED);
watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
while (1) // and busy-loop so that WD causes
; // a reset and app start.
}
putch(STK_INSYNC);
}
static void getNch(uint8_t count) {
do getch(); while (--count);
verifySpace();
}
typedef uint16_t pagelen_t;
static const char entry_magic_send [] PROGMEM = "start\n";
static const char entry_magic_receive[] PROGMEM = "w25x20cl_enter\n";
static const char entry_magic_cfm [] PROGMEM = "w25x20cl_cfm\n";
struct block_t;
extern struct block_t *block_buffer;
//! @brief Enter an STK500 compatible Optiboot boot loader waiting for flashing the languages to an external flash memory.
void optiboot_w25x20cl_enter()
{
if (boot_app_flags & BOOT_APP_FLG_USER0) return;
uint8_t ch;
uint8_t rampz = 0;
register uint16_t address = 0;
register pagelen_t length;
// Use the planner's queue for the receive / transmit buffers.
// uint8_t *buff = (uint8_t*)block_buffer;
uint8_t buff[260];
// bitmap of pages to be written. Bit is set to 1 if the page has already been erased.
uint8_t pages_erased = 0;
// Handshake sequence: Initialize the serial line, flush serial line, send magic, receive magic.
// If the magic is not received on time, or it is not received correctly, continue to the application.
{
watchdogReset();
unsigned long boot_timeout = 2000000;
unsigned long boot_timer = 0;
const char *ptr = entry_magic_send;
const char *end = strlen_P(entry_magic_send) + ptr;
// Initialize the serial line.
UCSR0A |= (1 << U2X0);
UBRR0L = (((float)(F_CPU))/(((float)(115200))*8.0)-1.0+0.5);
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
// Flush the serial line.
while (RECV_READY) {
watchdogReset();
// Dummy register read (discard)
(void)(*(char *)UDR0);
}
// Send the initial magic string.
while (ptr != end)
putch(pgm_read_byte(ptr ++));
watchdogReset();
// Wait for one second until a magic string (constant entry_magic) is received
// from the serial line.
ptr = entry_magic_receive;
end = strlen_P(entry_magic_receive) + ptr;
while (ptr != end) {
while (! RECV_READY) {
watchdogReset();
delayMicroseconds(1);
if (++ boot_timer > boot_timeout)
// Timeout expired, continue with the application.
return;
}
ch = UDR0;
if (pgm_read_byte(ptr ++) != ch)
// Magic was not received correctly, continue with the application
return;
watchdogReset();
}
// Send the cfm magic string.
ptr = entry_magic_cfm;
while (ptr != end)
putch(pgm_read_byte(ptr ++));
}
spi_init();
w25x20cl_init();
watchdogConfig(WATCHDOG_OFF);
/* Forever loop: exits by causing WDT reset */
for (;;) {
/* get character from UART */
ch = getch();
if(ch == STK_GET_PARAMETER) {
unsigned char which = getch();
verifySpace();
/*
* Send optiboot version as "SW version"
* Note that the references to memory are optimized away.
*/
if (which == STK_SW_MINOR) {
putch(optiboot_version & 0xFF);
} else if (which == STK_SW_MAJOR) {
putch(optiboot_version >> 8);
} else {
/*
* GET PARAMETER returns a generic 0x03 reply for
* other parameters - enough to keep Avrdude happy
*/
putch(0x03);
}
}
else if(ch == STK_SET_DEVICE) {
// SET DEVICE is ignored
getNch(20);
}
else if(ch == STK_SET_DEVICE_EXT) {
// SET DEVICE EXT is ignored
getNch(5);
}
else if(ch == STK_LOAD_ADDRESS) {
// LOAD ADDRESS
uint16_t newAddress;
// Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
// Send the binary data by nibbles to avoid transmitting the ';' character.
newAddress = getch();
newAddress |= getch();
newAddress |= (((uint16_t)getch()) << 8);
newAddress |= (((uint16_t)getch()) << 8);
// Transfer top bit to LSB in rampz
if (newAddress & 0x8000)
rampz |= 0x01;
else
rampz &= 0xFE;
newAddress += newAddress; // Convert from word address to byte address
address = newAddress;
verifySpace();
}
else if(ch == STK_UNIVERSAL) {
// LOAD_EXTENDED_ADDRESS is needed in STK_UNIVERSAL for addressing more than 128kB
if ( AVR_OP_LOAD_EXT_ADDR == getch() ) {
// get address
getch(); // get '0'
rampz = (rampz & 0x01) | ((getch() << 1) & 0xff); // get address and put it in rampz
getNch(1); // get last '0'
// response
putch(0x00);
}
else {
// everything else is ignored
getNch(3);
putch(0x00);
}
}
/* Write memory, length is big endian and is in bytes */
else if(ch == STK_PROG_PAGE) {
// PROGRAM PAGE - we support flash programming only, not EEPROM
uint8_t desttype;
uint8_t *bufPtr;
pagelen_t savelength;
// Read the page length, with the length transferred each nibble separately to work around
// the Prusa's USB to serial infamous semicolon issue.
length = ((pagelen_t)getch()) << 8;
length |= ((pagelen_t)getch()) << 8;
length |= getch();
length |= getch();
savelength = length;
// Read the destination type. It should always be 'F' as flash.
desttype = getch();
// read a page worth of contents
bufPtr = buff;
do *bufPtr++ = getch();
while (--length);
// Read command terminator, start reply
verifySpace();
if (desttype == 'E') {
while (1) ; // Error: wait for WDT
} else {
uint32_t addr = (((uint32_t)rampz) << 16) | address;
// During a single bootloader run, only erase a 64kB block once.
// An 8bit bitmask 'pages_erased' covers 512kB of FLASH memory.
if (address == 0 && (pages_erased & (1 << addr)) == 0) {
w25x20cl_wait_busy();
w25x20cl_enable_wr();
w25x20cl_block64_erase(addr);
pages_erased |= (1 << addr);
}
w25x20cl_wait_busy();
w25x20cl_enable_wr();
w25x20cl_page_program(addr, buff, savelength);
w25x20cl_wait_busy();
w25x20cl_disable_wr();
}
}
/* Read memory block mode, length is big endian. */
else if(ch == STK_READ_PAGE) {
uint32_t addr = (((uint32_t)rampz) << 16) | address;
register pagelen_t i;
// Read the page length, with the length transferred each nibble separately to work around
// the Prusa's USB to serial infamous semicolon issue.
length = ((pagelen_t)getch()) << 8;
length |= ((pagelen_t)getch()) << 8;
length |= getch();
length |= getch();
// Read the destination type. It should always be 'F' as flash. It is not checked.
(void)getch();
verifySpace();
w25x20cl_wait_busy();
w25x20cl_rd_data(addr, buff, length);
for (i = 0; i < length; ++ i)
putch(buff[i]);
}
/* Get device signature bytes */
else if(ch == STK_READ_SIGN) {
// READ SIGN - return what Avrdude wants to hear
verifySpace();
putch(W25X20CL_SIGNATURE_0);
putch(W25X20CL_SIGNATURE_1);
putch(W25X20CL_SIGNATURE_2);
}
else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
// Adaboot no-wait mod
watchdogConfig(WATCHDOG_16MS);
verifySpace();
}
else {
// This covers the response to commands like STK_ENTER_PROGMODE
verifySpace();
}
putch(STK_OK);
}
}

View File

@ -0,0 +1,6 @@
#ifndef OPTIBOOT_W25X20CL_H
#define OPTIBOOT_W25X20CL_H
extern void optiboot_w25x20cl_enter();
#endif /* OPTIBOOT_W25X20CL_H */

281
Firmware/pat9125.c Normal file
View File

@ -0,0 +1,281 @@
//pat9125.c
#include "pat9125.h"
#include <util/delay.h>
#include <avr/pgmspace.h>
#include "config.h"
#include <stdio.h>
//PAT9125 registers
#define PAT9125_PID1 0x00
#define PAT9125_PID2 0x01
#define PAT9125_MOTION 0x02
#define PAT9125_DELTA_XL 0x03
#define PAT9125_DELTA_YL 0x04
#define PAT9125_MODE 0x05
#define PAT9125_CONFIG 0x06
#define PAT9125_WP 0x09
#define PAT9125_SLEEP1 0x0a
#define PAT9125_SLEEP2 0x0b
#define PAT9125_RES_X 0x0d
#define PAT9125_RES_Y 0x0e
#define PAT9125_DELTA_XYH 0x12
#define PAT9125_SHUTTER 0x14
#define PAT9125_FRAME 0x17
#define PAT9125_ORIENTATION 0x19
#define PAT9125_BANK_SELECTION 0x7f
#ifdef PAT9125_SWSPI
#include "swspi.h"
#endif //PAT9125_SWSPI
#ifdef PAT9125_SWI2C
#include "swi2c.h"
#endif //PAT9125_SWI2C
uint8_t pat9125_PID1 = 0;
uint8_t pat9125_PID2 = 0;
int16_t pat9125_x = 0;
int16_t pat9125_y = 0;
uint8_t pat9125_b = 0;
uint8_t pat9125_s = 0;
// Init sequence, address & value.
const PROGMEM uint8_t pat9125_init_seq1[] = {
// Disable write protect.
PAT9125_WP, 0x5a,
// Set the X resolution to zero to let the sensor know that it could safely ignore movement in the X axis.
PAT9125_RES_X, PAT9125_XRES,
// Set the Y resolution to a maximum (or nearly a maximum).
PAT9125_RES_Y, PAT9125_YRES,
// Set 12-bit X/Y data format.
PAT9125_ORIENTATION, 0x04,
// PAT9125_ORIENTATION, 0x04 | (xinv?0x08:0) | (yinv?0x10:0), //!? direction switching does not work
// Now continues the magic sequence from the PAT912EL Application Note: Firmware Guides for Tracking Optimization.
0x5e, 0x08,
0x20, 0x64,
0x2b, 0x6d,
0x32, 0x2f,
// stopper
0x0ff
};
// Init sequence, address & value.
const PROGMEM uint8_t pat9125_init_seq2[] = {
// Magic sequence to enforce full frame rate of the sensor.
0x06, 0x028,
0x33, 0x0d0,
0x36, 0x0c2,
0x3e, 0x001,
0x3f, 0x015,
0x41, 0x032,
0x42, 0x03b,
0x43, 0x0f2,
0x44, 0x03b,
0x45, 0x0f2,
0x46, 0x022,
0x47, 0x03b,
0x48, 0x0f2,
0x49, 0x03b,
0x4a, 0x0f0,
0x58, 0x098,
0x59, 0x00c,
0x5a, 0x008,
0x5b, 0x00c,
0x5c, 0x008,
0x61, 0x010,
0x67, 0x09b,
0x6e, 0x022,
0x71, 0x007,
0x72, 0x008,
// stopper
0x0ff
};
uint8_t pat9125_rd_reg(uint8_t addr);
void pat9125_wr_reg(uint8_t addr, uint8_t data);
uint8_t pat9125_wr_reg_verify(uint8_t addr, uint8_t data);
extern FILE _uartout;
#define uartout (&_uartout)
uint8_t pat9125_init(void)
{
#ifdef PAT9125_SWSPI
swspi_init();
#endif //PAT9125_SWSPI
#ifdef PAT9125_SWI2C
swi2c_init();
#endif //PAT9125_SWI2C
// Verify that the sensor responds with its correct product ID.
pat9125_PID1 = pat9125_rd_reg(PAT9125_PID1);
pat9125_PID2 = pat9125_rd_reg(PAT9125_PID2);
if ((pat9125_PID1 != 0x31) || (pat9125_PID2 != 0x91))
{
pat9125_PID1 = pat9125_rd_reg(PAT9125_PID1);
pat9125_PID2 = pat9125_rd_reg(PAT9125_PID2);
if ((pat9125_PID1 != 0x31) || (pat9125_PID2 != 0x91))
return 0;
}
#ifdef PAT9125_NEW_INIT
// Switch to bank0, not allowed to perform OTS_RegWriteRead.
pat9125_wr_reg(PAT9125_BANK_SELECTION, 0);
// Software reset (i.e. set bit7 to 1). It will reset to 0 automatically.
// After the reset, OTS_RegWriteRead is not allowed.
pat9125_wr_reg(PAT9125_CONFIG, 0x97);
// Wait until the sensor reboots.
// Delay 1ms.
_delay_us(1000);
{
const uint8_t *ptr = pat9125_init_seq1;
for (;;) {
const uint8_t addr = pgm_read_byte_near(ptr ++);
if (addr == 0x0ff)
break;
if (! pat9125_wr_reg_verify(addr, pgm_read_byte_near(ptr ++)))
// Verification of the register write failed.
return 0;
}
}
// Delay 10ms.
_delay_ms(10);
// Switch to bank1, not allowed to perform OTS_RegWrite.
pat9125_wr_reg(PAT9125_BANK_SELECTION, 0x01);
{
const uint8_t *ptr = pat9125_init_seq2;
for (;;) {
const uint8_t addr = pgm_read_byte_near(ptr ++);
if (addr == 0x0ff)
break;
if (! pat9125_wr_reg_verify(addr, pgm_read_byte_near(ptr ++)))
// Verification of the register write failed.
return 0;
}
}
// Switch to bank0, not allowed to perform OTS_RegWriteRead.
pat9125_wr_reg(PAT9125_BANK_SELECTION, 0x00);
// Enable write protect.
pat9125_wr_reg(PAT9125_WP, 0x00);
pat9125_PID1 = pat9125_rd_reg(PAT9125_PID1);
pat9125_PID2 = pat9125_rd_reg(PAT9125_PID2);
#endif //PAT9125_NEW_INIT
pat9125_wr_reg(PAT9125_RES_X, PAT9125_XRES);
pat9125_wr_reg(PAT9125_RES_Y, PAT9125_YRES);
fprintf_P(uartout, PSTR("PAT9125_RES_X=%hhu\n"), pat9125_rd_reg(PAT9125_RES_X));
fprintf_P(uartout, PSTR("PAT9125_RES_Y=%hhu\n"), pat9125_rd_reg(PAT9125_RES_Y));
return 1;
}
uint8_t pat9125_update(void)
{
if ((pat9125_PID1 == 0x31) && (pat9125_PID2 == 0x91))
{
uint8_t ucMotion = pat9125_rd_reg(PAT9125_MOTION);
pat9125_b = pat9125_rd_reg(PAT9125_FRAME);
pat9125_s = pat9125_rd_reg(PAT9125_SHUTTER);
if (pat9125_PID1 == 0xff) return 0;
if (ucMotion & 0x80)
{
uint8_t ucXL = pat9125_rd_reg(PAT9125_DELTA_XL);
uint8_t ucYL = pat9125_rd_reg(PAT9125_DELTA_YL);
uint8_t ucXYH = pat9125_rd_reg(PAT9125_DELTA_XYH);
if (pat9125_PID1 == 0xff) return 0;
int16_t iDX = ucXL | ((ucXYH << 4) & 0xf00);
int16_t iDY = ucYL | ((ucXYH << 8) & 0xf00);
if (iDX & 0x800) iDX -= 4096;
if (iDY & 0x800) iDY -= 4096;
pat9125_x += iDX;
pat9125_y -= iDY; //negative number, because direction switching does not work
}
return 1;
}
return 0;
}
uint8_t pat9125_update_y(void)
{
if ((pat9125_PID1 == 0x31) && (pat9125_PID2 == 0x91))
{
uint8_t ucMotion = pat9125_rd_reg(PAT9125_MOTION);
if (pat9125_PID1 == 0xff) return 0;
if (ucMotion & 0x80)
{
uint8_t ucYL = pat9125_rd_reg(PAT9125_DELTA_YL);
uint8_t ucXYH = pat9125_rd_reg(PAT9125_DELTA_XYH);
if (pat9125_PID1 == 0xff) return 0;
int16_t iDY = ucYL | ((ucXYH << 8) & 0xf00);
if (iDY & 0x800) iDY -= 4096;
pat9125_y -= iDY; //negative number, because direction switching does not work
}
return 1;
}
return 0;
}
uint8_t pat9125_update_y2(void)
{
if ((pat9125_PID1 == 0x31) && (pat9125_PID2 == 0x91))
{
uint8_t ucMotion = pat9125_rd_reg(PAT9125_MOTION);
if (pat9125_PID1 == 0xff) return 0; //NOACK error
if (ucMotion & 0x80)
{
int8_t dy = pat9125_rd_reg(PAT9125_DELTA_YL);
if (pat9125_PID1 == 0xff) return 0; //NOACK error
pat9125_y -= dy; //negative number, because direction switching does not work
}
return 1;
}
return 0;
}
uint8_t pat9125_rd_reg(uint8_t addr)
{
uint8_t data = 0;
#ifdef PAT9125_SWSPI
swspi_start();
swspi_tx(addr & 0x7f);
data = swspi_rx();
swspi_stop();
#endif //PAT9125_SWSPI
#ifdef PAT9125_SWI2C
if (!swi2c_readByte_A8(PAT9125_I2C_ADDR, addr, &data)) //NO ACK error
{
pat9125_PID1 = 0xff;
pat9125_PID2 = 0xff;
return 0;
}
#endif //PAT9125_SWI2C
return data;
}
void pat9125_wr_reg(uint8_t addr, uint8_t data)
{
#ifdef PAT9125_SWSPI
swspi_start();
swspi_tx(addr | 0x80);
swspi_tx(data);
swspi_stop();
#endif //PAT9125_SWSPI
#ifdef PAT9125_SWI2C
if (!swi2c_writeByte_A8(PAT9125_I2C_ADDR, addr, &data)) //NO ACK error
{
pat9125_PID1 = 0xff;
pat9125_PID2 = 0xff;
return;
}
#endif //PAT9125_SWI2C
}
uint8_t pat9125_wr_reg_verify(uint8_t addr, uint8_t data)
{
pat9125_wr_reg(addr, data);
return pat9125_rd_reg(addr) == data;
}

31
Firmware/pat9125.h Normal file
View File

@ -0,0 +1,31 @@
//pat9125.h
#ifndef PAT9125_H
#define PAT9125_H
#include <inttypes.h>
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
extern uint8_t pat9125_PID1;
extern uint8_t pat9125_PID2;
extern int16_t pat9125_x;
extern int16_t pat9125_y;
extern uint8_t pat9125_b;
extern uint8_t pat9125_s;
extern uint8_t pat9125_init(void);
extern uint8_t pat9125_update(void);
extern uint8_t pat9125_update_y(void);
extern uint8_t pat9125_update_y2(void);
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)
#endif //PAT9125_H

88
Firmware/pins.h Normal file
View File

@ -0,0 +1,88 @@
#ifndef PINS_H
#define PINS_H
#include "boards.h"
#define LARGE_FLASH true
/*****************************************************************
* Rambo Pin Assignments 1.3
******************************************************************/
#if MOTHERBOARD == BOARD_RAMBO_MINI_1_0 //200 - orig 102
#include "pins_Rambo_1_0.h"
#endif //MOTHERBOARD == BOARD_RAMBO_MINI_1_0
#if MOTHERBOARD == BOARD_RAMBO_MINI_1_3 //203 - orig 302
#include "pins_Rambo_1_3.h"
#endif //MOTHERBOARD == BOARD_RAMBO_MINI_1_3
#if MOTHERBOARD == BOARD_EINSY_1_0a //310 - new
#include "pins_Einsy_1_0.h"
#endif //MOTHERBOARD == BOARD_EINSY_1_0a
#ifndef KNOWN_BOARD
#error Unknown MOTHERBOARD value in configuration.h
#endif
//List of pins which to ignore when asked to change by gcode, 0 and 1 are RX and TX, do not mess with those!
#define _E0_PINS E0_STEP_PIN, E0_DIR_PIN, E0_ENABLE_PIN, HEATER_0_PIN,
#if EXTRUDERS > 1
#define _E1_PINS E1_STEP_PIN, E1_DIR_PIN, E1_ENABLE_PIN, HEATER_1_PIN,
#else
#define _E1_PINS
#endif
#if EXTRUDERS > 2
#define _E2_PINS E2_STEP_PIN, E2_DIR_PIN, E2_ENABLE_PIN, HEATER_2_PIN,
#else
#define _E2_PINS
#endif
#ifdef X_STOP_PIN
#if X_HOME_DIR < 0
#define X_MIN_PIN X_STOP_PIN
#define X_MAX_PIN -1
#else
#define X_MIN_PIN -1
#define X_MAX_PIN X_STOP_PIN
#endif
#endif
#ifdef Y_STOP_PIN
#if Y_HOME_DIR < 0
#define Y_MIN_PIN Y_STOP_PIN
#define Y_MAX_PIN -1
#else
#define Y_MIN_PIN -1
#define Y_MAX_PIN Y_STOP_PIN
#endif
#endif
#ifdef Z_STOP_PIN
#if Z_HOME_DIR < 0
#define Z_MIN_PIN Z_STOP_PIN
#define Z_MAX_PIN -1
#else
#define Z_MIN_PIN -1
#define Z_MAX_PIN Z_STOP_PIN
#endif
#endif
#ifdef DISABLE_MAX_ENDSTOPS
#define X_MAX_PIN -1
#define Y_MAX_PIN -1
#define Z_MAX_PIN -1
#endif
#ifdef DISABLE_MIN_ENDSTOPS
#define X_MIN_PIN -1
#define Y_MIN_PIN -1
#define Z_MIN_PIN -1
#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, PS_ON_PIN, \
HEATER_BED_PIN, FAN_PIN, \
_E0_PINS _E1_PINS _E2_PINS \
analogInputToDigitalPin(TEMP_0_PIN), analogInputToDigitalPin(TEMP_1_PIN), analogInputToDigitalPin(TEMP_2_PIN), analogInputToDigitalPin(TEMP_BED_PIN) }
#endif //__PINS_H

158
Firmware/pins_Einsy_1_0.h Normal file
View File

@ -0,0 +1,158 @@
/*****************************************************************
* EINSY Rambo 1.0a Pin Assignments
******************************************************************/
#define ELECTRONICS "EINSy_10a"
#define KNOWN_BOARD
#ifndef __AVR_ATmega2560__
#error Oops! Make sure you have 'Arduino Mega 2560 or Rambo' selected from the 'Tools -> Boards' menu.
#endif
#define TMC2130
#define UVLO_SUPPORT
#define AMBIENT_THERMISTOR
#define PINDA_THERMISTOR
#define W25X20CL // external 256kB flash
#define BOOTAPP // bootloader support
#define SWI2C_SDA 20 //SDA on P3
#define SWI2C_SCL 21 //SCL on P3
#define X_TMC2130_CS 41
#define X_TMC2130_DIAG 64 // !!! changed from 40 (EINY03)
#define X_STEP_PIN 37
#define X_DIR_PIN 49
#define X_MIN_PIN 12
//#define X_MAX_PIN 30
//#define X_MIN_PIN X_TMC2130_DIAG
#define X_MAX_PIN X_TMC2130_DIAG
#define X_ENABLE_PIN 29
#define X_MS1_PIN -1
#define X_MS2_PIN -1
#define Y_TMC2130_CS 39
#define Y_TMC2130_DIAG 69
#define Y_STEP_PIN 36
#define Y_DIR_PIN 48
#define Y_MIN_PIN 11
//#define Y_MAX_PIN 24
//#define Y_MIN_PIN Y_TMC2130_DIAG
#define Y_MAX_PIN Y_TMC2130_DIAG
#define Y_ENABLE_PIN 28
#define Y_MS1_PIN -1
#define Y_MS2_PIN -1
#define Z_TMC2130_CS 67
#define Z_TMC2130_DIAG 68
#define Z_STEP_PIN 35
#define Z_DIR_PIN 47
#define Z_MIN_PIN 10
#define Z_MAX_PIN 23
//#define Z_MAX_PIN Z_TMC2130_DIAG
#define Z_ENABLE_PIN 27
#define Z_MS1_PIN -1
#define Z_MS2_PIN -1
#define HEATER_BED_PIN 4 //PG5
#define TEMP_BED_PIN 2 //A2
#define HEATER_0_PIN 3 //PE5
#define TEMP_0_PIN 0 //A0
#define HEATER_1_PIN -1
#define TEMP_1_PIN 1 //A1
#define HEATER_2_PIN -1
#define TEMP_2_PIN -1
#define TEMP_AMBIENT_PIN 5 //A5
#define TEMP_PINDA_PIN 3 //A3
#define VOLT_PWR_PIN 4 //A4
#define VOLT_BED_PIN 9 //A9
#define E0_TMC2130_CS 66
#define E0_TMC2130_DIAG 65
#define E0_STEP_PIN 34
#define E0_DIR_PIN 43
#define E0_ENABLE_PIN 26
#define E0_MS1_PIN -1
#define E0_MS2_PIN -1
#define SDPOWER -1
#define SDSS 77
#define LED_PIN 13
#define FAN_PIN 6
#define FAN_1_PIN -1
#define PS_ON_PIN -1
#define KILL_PIN -1 // 80 with Smart Controller LCD
#define SUICIDE_PIN -1 // PIN that has to be turned on right after start, to keep power flowing.
//#define KILL_PIN 32
//#define LCD_BL_PIN 5 //backlight control pin
#define BEEPER 84 // Beeper on AUX-4
#define LCD_PINS_RS 82
#define LCD_PINS_ENABLE 61 // !!! changed from 18 (EINY03)
#define LCD_PINS_D4 59 // !!! changed from 19 (EINY03)
#define LCD_PINS_D5 70
#define LCD_PINS_D6 85
#define LCD_PINS_D7 71
//buttons are directly attached using AUX-2
#define BTN_EN1 72
#define BTN_EN2 14
#define BTN_ENC 9 // the click
#define SDCARDDETECT 15
#define TACH_0 79 // !!! changed from 81 (EINY03)
#define TACH_1 80
#define IR_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8)
// Support for an 8 bit logic analyzer, for example the Saleae.
// Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop.
#define LOGIC_ANALYZER_CH0 X_MIN_PIN // PB6
#define LOGIC_ANALYZER_CH1 Y_MIN_PIN // PB5
#define LOGIC_ANALYZER_CH2 53 // PB0 (PROC_nCS)
// Channels 3-7 are slow, they could generate
// 0.889Mhz waveform with a software loop and interrupt locking,
// 1.333MHz waveform without interrupt locking.
#define LOGIC_ANALYZER_CH3 73 // PJ3
// PK0 has no Arduino digital pin assigned, so we set it directly.
#define WRITE_LOGIC_ANALYZER_CH4(value) if (value) PORTK |= (1 << 0); else PORTK &= ~(1 << 0) // PK0
#define LOGIC_ANALYZER_CH5 16 // PH0 (RXD2)
#define LOGIC_ANALYZER_CH6 17 // PH1 (TXD2)
#define LOGIC_ANALYZER_CH7 76 // PJ5
#define LOGIC_ANALYZER_CH0_ENABLE do { SET_OUTPUT(LOGIC_ANALYZER_CH0); WRITE(LOGIC_ANALYZER_CH0, false); } while (0)
#define LOGIC_ANALYZER_CH1_ENABLE do { SET_OUTPUT(LOGIC_ANALYZER_CH1); WRITE(LOGIC_ANALYZER_CH1, false); } while (0)
#define LOGIC_ANALYZER_CH2_ENABLE do { SET_OUTPUT(LOGIC_ANALYZER_CH2); WRITE(LOGIC_ANALYZER_CH2, false); } while (0)
#define LOGIC_ANALYZER_CH3_ENABLE do { SET_OUTPUT(LOGIC_ANALYZER_CH3); WRITE(LOGIC_ANALYZER_CH3, false); } while (0)
#define LOGIC_ANALYZER_CH4_ENABLE do { DDRK |= 1 << 0; WRITE_LOGIC_ANALYZER_CH4(false); } while (0)
#define LOGIC_ANALYZER_CH5_ENABLE do { cbi(UCSR2B, TXEN2); cbi(UCSR2B, RXEN2); cbi(UCSR2B, RXCIE2); SET_OUTPUT(LOGIC_ANALYZER_CH5); WRITE(LOGIC_ANALYZER_CH5, false); } while (0)
#define LOGIC_ANALYZER_CH6_ENABLE do { cbi(UCSR2B, TXEN2); cbi(UCSR2B, RXEN2); cbi(UCSR2B, RXCIE2); SET_OUTPUT(LOGIC_ANALYZER_CH6); WRITE(LOGIC_ANALYZER_CH6, false); } while (0)
#define LOGIC_ANALYZER_CH7_ENABLE do { SET_OUTPUT(LOGIC_ANALYZER_CH7); WRITE(LOGIC_ANALYZER_CH7, false); } while (0)
// Async output on channel 5 of the logical analyzer.
// Baud rate 2MBit, 9 bits, 1 stop bit.
#define LOGIC_ANALYZER_SERIAL_TX_ENABLE do { UBRR2H = 0; UBRR2L = 0; UCSR2B = (1 << TXEN2) | (1 << UCSZ02); UCSR2C = 0x06; } while (0)
// Non-checked (quicker) variant. Use it if you are sure that the transmit buffer is already empty.
#define LOGIC_ANALYZER_SERIAL_TX_WRITE_NC(C) do { if (C & 0x100) UCSR2B |= 1; else UCSR2B &= ~1; UDR2 = C; } while (0)
#define LOGIC_ANALYZER_SERIAL_TX_WRITE(C) do { \
/* Wait for empty transmit buffer */ \
while (!(UCSR2A & (1<<UDRE2))); \
/* Put data into buffer, sends the data */ \
LOGIC_ANALYZER_SERIAL_TX_WRITE_NC(C); \
} while (0)

130
Firmware/pins_Rambo_1_0.h Normal file
View File

@ -0,0 +1,130 @@
/*****************************************************************
* Rambo mini 1.0 Pin Assignments
******************************************************************/
#define ELECTRONICS "RAMBo10a"
#define KNOWN_BOARD
#ifndef __AVR_ATmega2560__
#error Oops! Make sure you have 'Arduino Mega 2560 or Rambo' selected from the 'Tools -> Boards' menu.
#endif
#define PINDA_THERMISTOR
#define SWI2C_SDA 20 //SDA on P3
#define SWI2C_SCL 84 //PH2 on P3, sensor cable must be rewired
#define X_STEP_PIN 37
#define X_DIR_PIN 48
#define X_MIN_PIN 12
#define X_MAX_PIN -1
#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 -1
#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 23
#define Z_ENABLE_PIN 27
#define Z_MS1_PIN 68
#define Z_MS2_PIN 67
#define HEATER_BED_PIN 4 //PG5
#define TEMP_BED_PIN 2 //A2
#define HEATER_0_PIN 3 //PE5
#define TEMP_0_PIN 0 //A0
#define HEATER_1_PIN -1
#define TEMP_1_PIN -1 //A1
#define HEATER_2_PIN -1
#define TEMP_2_PIN -1
#define TEMP_AMBIENT_PIN 6 //A6
#define TEMP_PINDA_PIN 1 //A1
#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
#ifdef SNMM
#define E_MUX0_PIN 17
#define E_MUX1_PIN 16
#endif
#define MOTOR_CURRENT_PWM_XY_PIN 46
#define MOTOR_CURRENT_PWM_Z_PIN 45
#define MOTOR_CURRENT_PWM_E_PIN 44
#define SDPOWER -1
#define SDSS 53
#define LED_PIN 13
#define FAN_PIN 6
#define FAN_1_PIN -1
#define PS_ON_PIN -1
#define KILL_PIN -1 // 80 with Smart Controller LCD
#define SUICIDE_PIN -1 // PIN that has to be turned on right after start, to keep power flowing.
#define TACH_0 30 // noctua extruder fan
//#define KILL_PIN 32
#define BEEPER 78 // Beeper on AUX-4
#define LCD_PINS_RS 38
#define LCD_PINS_ENABLE 5
#define LCD_PINS_D4 14
#define LCD_PINS_D5 15
#define LCD_PINS_D6 32
#define LCD_PINS_D7 31
//buttons are directly attached using AUX-2
#define BTN_EN1 80
#define BTN_EN2 73
#define BTN_ENC 21 // the click
#define SDCARDDETECT 72
#define IR_SENSOR_PIN 20 //idler sensor
// Support for an 8 bit logic analyzer, for example the Saleae.
// Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop.
#define LOGIC_ANALYZER_CH0 X_MIN_PIN // PB6
#define LOGIC_ANALYZER_CH1 Y_MIN_PIN // PB5
#define LOGIC_ANALYZER_CH2 53 // PB0 (PROC_nCS)
// Channels 3-7 are slow, they could generate
// 0.889Mhz waveform with a software loop and interrupt locking,
// 1.333MHz waveform without interrupt locking.
#define LOGIC_ANALYZER_CH3 73 // PJ3
// PK0 has no Arduino digital pin assigned, so we set it directly.
#define WRITE_LOGIC_ANALYZER_CH4(value) if (value) PORTK |= (1 << 0); else PORTK &= ~(1 << 0) // PK0
#define LOGIC_ANALYZER_CH5 16 // PH0 (RXD2)
#define LOGIC_ANALYZER_CH6 17 // PH1 (TXD2)
#define LOGIC_ANALYZER_CH7 76 // PJ5
#define LOGIC_ANALYZER_CH0_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH0)
#define LOGIC_ANALYZER_CH1_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH1)
#define LOGIC_ANALYZER_CH2_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH2)
#define LOGIC_ANALYZER_CH3_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH3)
#define LOGIC_ANALYZER_CH4_ENABLE do { DDRK |= 1 << 0; } while (0)
#define LOGIC_ANALYZER_CH5_ENABLE do { cbi(UCSR2B, TXEN2); cbi(UCSR2B, RXEN2); cbi(UCSR2B, RXCIE2); SET_OUTPUT(LOGIC_ANALYZER_CH5); } while (0)
#define LOGIC_ANALYZER_CH6_ENABLE do { cbi(UCSR2B, TXEN2); cbi(UCSR2B, RXEN2); cbi(UCSR2B, RXCIE2); SET_OUTPUT(LOGIC_ANALYZER_CH6); } while (0)
#define LOGIC_ANALYZER_CH7_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH7)

136
Firmware/pins_Rambo_1_3.h Normal file
View File

@ -0,0 +1,136 @@
/*****************************************************************
* Rambo mini 1.3 Pin Assignments
******************************************************************/
#define ELECTRONICS "RAMBo13a"
#define KNOWN_BOARD
#ifndef __AVR_ATmega2560__
#error Oops! Make sure you have 'Arduino Mega 2560 or Rambo' selected from the 'Tools -> Boards' menu.
#endif
#define PINDA_THERMISTOR
#define SWI2C_SDA 20 //SDA on P3
#define SWI2C_SCL 21 //SCL on P3
#ifdef MICROMETER_LOGGING
#define D_DATACLOCK 24 //Y_MAX (green)
#define D_DATA 30 //X_MAX (blue)
#define D_REQUIRE 23 //Z_MAX (white)
#endif //MICROMETER_LOGGING
#define X_STEP_PIN 37
#define X_DIR_PIN 48
#define X_MIN_PIN 12
#define X_MAX_PIN -1
#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 -1
#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 23
#define Z_ENABLE_PIN 27
#define Z_MS1_PIN 68
#define Z_MS2_PIN 67
#define HEATER_BED_PIN 4 //PG5
#define TEMP_BED_PIN 2 //A2
#define HEATER_0_PIN 3 //PE5
#define TEMP_0_PIN 0 //A0
#define HEATER_1_PIN -1
#define TEMP_1_PIN -1 //A1
#define HEATER_2_PIN -1
#define TEMP_2_PIN -1
#define TEMP_AMBIENT_PIN 6 //A6
#define TEMP_PINDA_PIN 1 //A1
#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
#ifdef SNMM
#define E_MUX0_PIN 17
#define E_MUX1_PIN 16
#endif
#define MOTOR_CURRENT_PWM_XY_PIN 46
#define MOTOR_CURRENT_PWM_Z_PIN 45
#define MOTOR_CURRENT_PWM_E_PIN 44
#define SDPOWER -1
#define SDSS 53
#define LED_PIN 13
#define FAN_PIN 6
#define FAN_1_PIN -1
#define PS_ON_PIN -1
#define KILL_PIN -1 // 80 with Smart Controller LCD
#define SUICIDE_PIN -1 // PIN that has to be turned on right after start, to keep power flowing.
#define TACH_0 30 // noctua extruder fan
//#define KILL_PIN 32
#define BEEPER 84 // Beeper on AUX-4
#define LCD_PINS_RS 82
#define LCD_PINS_ENABLE 18
#define LCD_PINS_D4 19
#define LCD_PINS_D5 70
#define LCD_PINS_D6 85
#define LCD_PINS_D7 71
//buttons are directly attached using AUX-2
#define BTN_EN1 72
#define BTN_EN2 14
#define BTN_ENC 9 // the click
#define SDCARDDETECT 15
#define IR_SENSOR_PIN 20 //idler sensor
// Support for an 8 bit logic analyzer, for example the Saleae.
// Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop.
#define LOGIC_ANALYZER_CH0 X_MIN_PIN // PB6
#define LOGIC_ANALYZER_CH1 Y_MIN_PIN // PB5
#define LOGIC_ANALYZER_CH2 53 // PB0 (PROC_nCS)
// Channels 3-7 are slow, they could generate
// 0.889Mhz waveform with a software loop and interrupt locking,
// 1.333MHz waveform without interrupt locking.
#define LOGIC_ANALYZER_CH3 73 // PJ3
// PK0 has no Arduino digital pin assigned, so we set it directly.
#define WRITE_LOGIC_ANALYZER_CH4(value) if (value) PORTK |= (1 << 0); else PORTK &= ~(1 << 0) // PK0
#define LOGIC_ANALYZER_CH5 16 // PH0 (RXD2)
#define LOGIC_ANALYZER_CH6 17 // PH1 (TXD2)
#define LOGIC_ANALYZER_CH7 76 // PJ5
#define LOGIC_ANALYZER_CH0_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH0)
#define LOGIC_ANALYZER_CH1_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH1)
#define LOGIC_ANALYZER_CH2_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH2)
#define LOGIC_ANALYZER_CH3_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH3)
#define LOGIC_ANALYZER_CH4_ENABLE do { DDRK |= 1 << 0; } while (0)
#define LOGIC_ANALYZER_CH5_ENABLE do { cbi(UCSR2B, TXEN2); cbi(UCSR2B, RXEN2); cbi(UCSR2B, RXCIE2); SET_OUTPUT(LOGIC_ANALYZER_CH5); } while (0)
#define LOGIC_ANALYZER_CH6_ENABLE do { cbi(UCSR2B, TXEN2); cbi(UCSR2B, RXEN2); cbi(UCSR2B, RXCIE2); SET_OUTPUT(LOGIC_ANALYZER_CH6); } while (0)
#define LOGIC_ANALYZER_CH7_ENABLE SET_OUTPUT(LOGIC_ANALYZER_CH7)

1375
Firmware/planner.cpp Normal file

File diff suppressed because it is too large Load Diff

263
Firmware/planner.h Normal file
View File

@ -0,0 +1,263 @@
/*
planner.h - buffers movement commands and manages the acceleration profile plan
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Grbl 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.
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
// This module is to be considered a sub-module of stepper.c. Please don't include
// this file from any other module.
#ifndef planner_h
#define planner_h
#include "Marlin.h"
#ifdef ENABLE_AUTO_BED_LEVELING
#include "vector_3.h"
#endif // ENABLE_AUTO_BED_LEVELING
enum BlockFlag {
// Planner flag to recalculate trapezoids on entry junction.
// This flag has an optimization purpose only.
BLOCK_FLAG_RECALCULATE = 1,
// Planner flag for nominal speed always reached. That means, the segment is long enough, that the nominal speed
// may be reached if accelerating from a safe speed (in the regard of jerking from zero speed).
BLOCK_FLAG_NOMINAL_LENGTH = 2,
// If set, the machine will start from a halt at the start of this block,
// respecting the maximum allowed jerk.
BLOCK_FLAG_START_FROM_FULL_HALT = 4,
// If set, the stepper interrupt expects, that the number of steps to tick will be lower
// than 32767, therefore the DDA algorithm may run with 16bit resolution only.
// In addition, the stepper routine will not do any end stop checking for higher performance.
BLOCK_FLAG_DDA_LOWRES = 8,
};
union dda_isteps_t
{
int32_t wide;
struct {
int16_t lo;
int16_t hi;
};
};
union dda_usteps_t
{
uint32_t wide;
struct {
uint16_t lo;
uint16_t hi;
};
};
// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in
// the source g-code and may never actually be reached if acceleration management is active.
typedef struct {
// Fields used by the bresenham algorithm for tracing the line
// steps_x.y,z, step_event_count, acceleration_rate, direction_bits and active_extruder are set by plan_buffer_line().
dda_isteps_t steps_x, steps_y, steps_z, steps_e; // Step count along each axis
dda_usteps_t step_event_count; // The number of step events required to complete this block
long acceleration_rate; // The acceleration rate used for acceleration calculation
unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
unsigned char active_extruder; // Selects the active extruder
// accelerate_until and decelerate_after are set by calculate_trapezoid_for_block() and they need to be synchronized with the stepper interrupt controller.
long accelerate_until; // The index of the step event on which to stop acceleration
long decelerate_after; // The index of the step event on which to start decelerating
// Fields used by the motion planner to manage acceleration
// float speed_x, speed_y, speed_z, speed_e; // Nominal mm/sec for each axis
// The nominal speed for this block in mm/sec.
// This speed may or may not be reached due to the jerk and acceleration limits.
float nominal_speed;
// Entry speed at previous-current junction in mm/sec, respecting the acceleration and jerk limits.
// The entry speed limit of the current block equals the exit speed of the preceding block.
float entry_speed;
// Maximum allowable junction entry speed in mm/sec. This value is also a maximum exit speed of the previous block.
float max_entry_speed;
// The total travel of this block in mm
float millimeters;
// acceleration mm/sec^2
float acceleration;
// Bit flags defined by the BlockFlag enum.
uint8_t flag;
// Settings for the trapezoid generator (runs inside an interrupt handler).
// Changing the following values in the planner needs to be synchronized with the interrupt handler by disabling the interrupts.
//FIXME nominal_rate, initial_rate and final_rate are limited to uint16_t by MultiU24X24toH16 in the stepper interrupt anyway!
unsigned long nominal_rate; // The nominal step rate for this block in step_events/sec
unsigned long initial_rate; // The jerk-adjusted step rate at start of block
unsigned long final_rate; // The minimal rate at exit
unsigned long acceleration_st; // acceleration steps/sec^2
//FIXME does it have to be unsigned long? Probably uint8_t would be just fine.
unsigned long fan_speed;
volatile char busy;
// Pre-calculated division for the calculate_trapezoid_for_block() routine to run faster.
float speed_factor;
#ifdef LIN_ADVANCE
bool use_advance_lead;
unsigned long abs_adv_steps_multiplier8; // Factorised by 2^8 to avoid float
#endif
uint16_t sdlen;
} block_t;
#ifdef LIN_ADVANCE
extern float extruder_advance_k, advance_ed_ratio;
#endif
#ifdef ENABLE_AUTO_BED_LEVELING
// this holds the required transform to compensate for bed level
extern matrix_3x3 plan_bed_level_matrix;
#endif // #ifdef ENABLE_AUTO_BED_LEVELING
// Initialize the motion plan subsystem
void plan_init();
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in
// millimaters. Feed rate specifies the speed of the motion.
#ifdef ENABLE_AUTO_BED_LEVELING
void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate, const uint8_t &extruder);
// Get the position applying the bed level matrix if enabled
vector_3 plan_get_position();
#else
/// Extracting common call of
/// plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[3], ...
/// saves almost 5KB.
/// The performance penalty is negligible, since these planned lines are usually maintenance moves with the extruder.
void plan_buffer_line_curposXYZE(float feed_rate, uint8_t extruder);
void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate, uint8_t extruder);
//void plan_buffer_line(const float &x, const float &y, const float &z, const float &e, float feed_rate, const uint8_t &extruder);
#endif // ENABLE_AUTO_BED_LEVELING
// Set position. Used for G92 instructions.
//#ifdef ENABLE_AUTO_BED_LEVELING
void plan_set_position(float x, float y, float z, const float &e);
//#else
//void plan_set_position(const float &x, const float &y, const float &z, const float &e);
//#endif // ENABLE_AUTO_BED_LEVELING
void plan_set_z_position(const float &z);
void plan_set_e_position(const float &e);
extern bool e_active();
void check_axes_activity();
// Use M203 to override by software
extern float* max_feedrate;
// Use M201 to override by software
extern unsigned long* max_acceleration_units_per_sq_second;
extern unsigned long axis_steps_per_sqr_second[NUM_AXIS];
extern long position[NUM_AXIS];
extern uint8_t maxlimit_status;
#ifdef AUTOTEMP
extern bool autotemp_enabled;
extern float autotemp_max;
extern float autotemp_min;
extern float autotemp_factor;
#endif
extern block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions
// Index of the next block to be pushed into the planner queue.
extern volatile unsigned char block_buffer_head;
// Index of the first block in the planner queue.
// This is the block, which is being currently processed by the stepper routine,
// or which is first to be processed by the stepper routine.
extern volatile unsigned char block_buffer_tail;
// Called when the current block is no longer needed. Discards the block and makes the memory
// available for new blocks.
FORCE_INLINE void plan_discard_current_block()
{
if (block_buffer_head != block_buffer_tail) {
block_buffer_tail = (block_buffer_tail + 1) & (BLOCK_BUFFER_SIZE - 1);
}
}
// Gets the current block. This is the block to be exectuted by the stepper routine.
// Mark this block as busy, so its velocities and acceperations will be no more recalculated
// by the planner routine.
// Returns NULL if buffer empty
FORCE_INLINE block_t *plan_get_current_block()
{
if (block_buffer_head == block_buffer_tail) {
return(NULL);
}
block_t *block = &block_buffer[block_buffer_tail];
block->busy = true;
return(block);
}
// Returns true if the buffer has a queued block, false otherwise
FORCE_INLINE bool blocks_queued() {
return (block_buffer_head != block_buffer_tail);
}
//return the nr of buffered moves
FORCE_INLINE uint8_t moves_planned() {
return (block_buffer_head + BLOCK_BUFFER_SIZE - block_buffer_tail) & (BLOCK_BUFFER_SIZE - 1);
}
FORCE_INLINE bool planner_queue_full() {
unsigned char next_block_index = block_buffer_head;
if (++ next_block_index == BLOCK_BUFFER_SIZE)
next_block_index = 0;
return block_buffer_tail == next_block_index;
}
// Abort the stepper routine, clean up the block queue,
// wait for the steppers to stop,
// update planner's current position and the current_position of the front end.
extern void planner_abort_hard();
#ifdef PREVENT_DANGEROUS_EXTRUDE
void set_extrude_min_temp(float temp);
#endif
void reset_acceleration_rates();
#endif
void update_mode_profile();
unsigned char number_of_blocks();
// #define PLANNER_DIAGNOSTICS
#ifdef PLANNER_DIAGNOSTICS
// Diagnostic functions to display planner buffer underflow on the display.
extern uint8_t planner_queue_min();
// Diagnostic function: Reset the minimum planner segments.
extern void planner_queue_min_reset();
#endif /* PLANNER_DIAGNOSTICS */
extern void planner_add_sd_length(uint16_t sdlen);
extern uint16_t planner_calc_sd_length();

40
Firmware/printers.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef PRINTERS_H
#define PRINTERS_H
#define PRINTER_UNKNOWN 0
// *** MK1
#define PRINTER_MK1 100
#define PRINTER_MK1_NAME "MK1"
// *** MK2
#define PRINTER_MK2 200
#define PRINTER_MK2_NAME "MK2"
#define PRINTER_MK2_SNMM 201 // better is "10200"
#define PRINTER_MK2_SNMM_NAME "MK2MM" // better is "MK2MMU1"
// *** MK2S ??? is same as "MK2" ???
#define PRINTER_MK2S 202
#define PRINTER_MK2S_NAME "MK2S"
#define PRINTER_MK2S_SNMM 203 // better is "10202"
#define PRINTER_MK2S_SNMM_NAME "MK2SMM" // better is "MK2SMMU1"
// *** MK2.5
#define PRINTER_MK25 250
#define PRINTER_MK25_NAME "MK2.5"
#define PRINTER_MK25_MMU2 20250
#define PRINTER_MK25_MMU2_NAME "MK2.5MMU2"
// *** MK2.5S
#define PRINTER_MK25S 252
#define PRINTER_MK25S_NAME "MK2.5S"
#define PRINTER_MK25S_MMU2 20252
#define PRINTER_MK25S_MMU2_NAME "MK2.5SMMU2S"
// *** MK3
#define PRINTER_MK3 300
#define PRINTER_MK3_NAME "MK3"
#define PRINTER_MK3_MMU2 20300
#define PRINTER_MK3_MMU2_NAME "MK3MMU2"
// *** MK3S
#define PRINTER_MK3S 302
#define PRINTER_MK3S_NAME "MK3S"
#define PRINTER_MK3S_MMU2 20302
#define PRINTER_MK3S_MMU2_NAME "MK3SMMU2S"
#endif //PRINTERS_H

1930
Firmware/qr_solve.cpp Normal file

File diff suppressed because it is too large Load Diff

22
Firmware/qr_solve.h Normal file
View File

@ -0,0 +1,22 @@
#include "Configuration.h"
#ifdef AUTO_BED_LEVELING_GRID
void daxpy ( int n, double da, double dx[], int incx, double dy[], int incy );
double ddot ( int n, double dx[], int incx, double dy[], int incy );
double dnrm2 ( int n, double x[], int incx );
void dqrank ( double a[], int lda, int m, int n, double tol, int *kr,
int jpvt[], double qraux[] );
void dqrdc ( double a[], int lda, int n, int p, double qraux[], int jpvt[],
double work[], int job );
int dqrls ( double a[], int lda, int m, int n, double tol, int *kr, double b[],
double x[], double rsd[], int jpvt[], double qraux[], int itask );
void dqrlss ( double a[], int lda, int m, int n, int kr, double b[], double x[],
double rsd[], int jpvt[], double qraux[] );
int dqrsl ( double a[], int lda, int n, int k, double qraux[], double y[],
double qy[], double qty[], double b[], double rsd[], double ab[], int job );
void dscal ( int n, double sa, double x[], int incx );
void dswap ( int n, double x[], int incx, double y[], int incy );
double *qr_solve ( int m, int n, double a[], double b[] );
#endif

65
Firmware/rbuf.c Normal file
View File

@ -0,0 +1,65 @@
//rbuf.c
#include "rbuf.h"
//#include <avr/interrupt.h>
void rbuf_ini(uint8_t* ptr, uint8_t l)
{
ptr[0] = l;
ptr[1] = 0;
ptr[2] = 0;
}
//lock/unlock macros
//#define _lock() uint8_t _sreg = SREG; cli();
//#define _unlock() SREG = _sreg;
#define _lock()
#define _unlock()
//put single byte to buffer
int rbuf_put(uint8_t* ptr, uint8_t b)
{
//#ifdef _NO_ASM
_lock(); //lock
uint8_t buf_w = ptr[1]; //get write index
uint8_t buf_r = ptr[2]; //get read index
_unlock(); //unlock
ptr[4 + buf_w] = b; //store byte to buffer
buf_w++; //incerment write index
uint8_t buf_l = ptr[0]; //get length
if (buf_w >= buf_l) buf_w = 0; //rotate write index
if (buf_w == buf_r) return -1; //return -1 to signal buffer full
ptr[1] = buf_w; //store write index
return 0; //return 0 to signal success
//#else //_NO_ASM
// TODO - optimized assembler version
// asm("movw r26, r24");
// asm("ld r18, X+");
// asm("cli");
// asm("ld r19, X+");
// asm("ld r20, X");
// asm("cp r19, r18");
// asm("brne .-6");*/
//#endif //_NO_ASM
}
//get single byte from buffer
int rbuf_get(uint8_t* ptr)
{
//#ifdef _NO_ASM
_lock(); //lock
uint8_t buf_w = ptr[1]; //get write index
uint8_t buf_r = ptr[2]; //get read index
_unlock(); //unlock
if (buf_r == buf_w) return -1; //return -1 to signal buffer empty
int ret = ptr[4 + buf_r]; //get byte from buffer
buf_r++; //increment read index
uint8_t buf_l = ptr[0]; //get length
if (buf_r >= buf_l) buf_r = 0; //rotate read index
ptr[2] = buf_r; //store read index
return ret; //return byte (0-255)
// return 0; //return 0 to signal success
//#else //_NO_ASM
// TODO - optimized assembler version
//#endif //_NO_ASM
}

27
Firmware/rbuf.h Normal file
View File

@ -0,0 +1,27 @@
//rbuf.h
#ifndef _RBUF_H
#define _RBUF_H
#include <inttypes.h>
#define rbuf_l(ptr) (ptr[0])
#define rbuf_w(ptr) (ptr[1])
#define rbuf_r(ptr) (ptr[2])
#define rbuf_empty(ptr) (ptr[1] == ptr[2])
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
extern void rbuf_ini(uint8_t* ptr, uint8_t len);
extern int rbuf_put(uint8_t* ptr, uint8_t val);
extern int rbuf_get(uint8_t* ptr);
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)
#endif //_RBUF_H

195
Firmware/sm4.c Normal file
View File

@ -0,0 +1,195 @@
//sm4.c - simple 4-axis stepper control
#include "sm4.h"
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <math.h>
#include "Arduino.h"
#include "boards.h"
#define false 0
#define true 1
#include "Configuration_prusa.h"
#ifdef NEW_XYZCAL
// Signal pinouts
// direction signal - MiniRambo
//#define X_DIR_PIN 48 //PL1 (-)
//#define Y_DIR_PIN 49 //PL0 (-)
//#define Z_DIR_PIN 47 //PL2 (-)
//#define E0_DIR_PIN 43 //PL6 (+)
//direction signal - EinsyRambo
//#define X_DIR_PIN 49 //PL0 (+)
//#define Y_DIR_PIN 48 //PL1 (-)
//#define Z_DIR_PIN 47 //PL2 (+)
//#define E0_DIR_PIN 43 //PL6 (-)
//step signal pinout - common for all rambo boards
//#define X_STEP_PIN 37 //PC0 (+)
//#define Y_STEP_PIN 36 //PC1 (+)
//#define Z_STEP_PIN 35 //PC2 (+)
//#define E0_STEP_PIN 34 //PC3 (+)
sm4_stop_cb_t sm4_stop_cb = 0;
sm4_update_pos_cb_t sm4_update_pos_cb = 0;
sm4_calc_delay_cb_t sm4_calc_delay_cb = 0;
uint16_t sm4_cpu_time = 0;
uint8_t sm4_get_dir(uint8_t axis)
{
switch (axis)
{
#if ((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3))
case 0: return (PORTL & 2)?0:1;
case 1: return (PORTL & 1)?0:1;
case 2: return (PORTL & 4)?0:1;
case 3: return (PORTL & 64)?1:0;
#elif ((MOTHERBOARD == BOARD_EINSY_1_0a))
case 0: return (PORTL & 1)?1:0;
case 1: return (PORTL & 2)?0:1;
case 2: return (PORTL & 4)?1:0;
case 3: return (PORTL & 64)?0:1;
#endif
}
return 0;
}
void sm4_set_dir(uint8_t axis, uint8_t dir)
{
switch (axis)
{
#if ((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3))
case 0: if (!dir) PORTL |= 2; else PORTL &= ~2; break;
case 1: if (!dir) PORTL |= 1; else PORTL &= ~1; break;
case 2: if (!dir) PORTL |= 4; else PORTL &= ~4; break;
case 3: if (dir) PORTL |= 64; else PORTL &= ~64; break;
#elif ((MOTHERBOARD == BOARD_EINSY_1_0a))
case 0: if (dir) PORTL |= 1; else PORTL &= ~1; break;
case 1: if (!dir) PORTL |= 2; else PORTL &= ~2; break;
case 2: if (dir) PORTL |= 4; else PORTL &= ~4; break;
case 3: if (!dir) PORTL |= 64; else PORTL &= ~64; break;
#endif
}
asm("nop");
}
uint8_t sm4_get_dir_bits(void)
{
register uint8_t dir_bits = 0;
register uint8_t portL = PORTL;
//TODO -optimize in asm
#if ((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3))
if (portL & 2) dir_bits |= 1;
if (portL & 1) dir_bits |= 2;
if (portL & 4) dir_bits |= 4;
if (portL & 64) dir_bits |= 8;
dir_bits ^= 0x07; //invert XYZ, do not invert E
#elif ((MOTHERBOARD == BOARD_EINSY_1_0a))
if (portL & 1) dir_bits |= 1;
if (portL & 2) dir_bits |= 2;
if (portL & 4) dir_bits |= 4;
if (portL & 64) dir_bits |= 8;
dir_bits ^= 0x0a; //invert YE, do not invert XZ
#endif
return dir_bits;
}
void sm4_set_dir_bits(uint8_t dir_bits)
{
register uint8_t portL = PORTL;
portL &= 0xb8; //set direction bits to zero
//TODO -optimize in asm
#if ((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3))
dir_bits ^= 0x07; //invert XYZ, do not invert E
if (dir_bits & 1) portL |= 2; //set X direction bit
if (dir_bits & 2) portL |= 1; //set Y direction bit
if (dir_bits & 4) portL |= 4; //set Z direction bit
if (dir_bits & 8) portL |= 64; //set E direction bit
#elif ((MOTHERBOARD == BOARD_EINSY_1_0a))
dir_bits ^= 0x0a; //invert YE, do not invert XZ
if (dir_bits & 1) portL |= 1; //set X direction bit
if (dir_bits & 2) portL |= 2; //set Y direction bit
if (dir_bits & 4) portL |= 4; //set Z direction bit
if (dir_bits & 8) portL |= 64; //set E direction bit
#endif
PORTL = portL;
asm("nop");
}
void sm4_do_step(uint8_t axes_mask)
{
#if ((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3) || (MOTHERBOARD == BOARD_EINSY_1_0a))
register uint8_t portC = PORTC & 0xf0;
PORTC = portC | (axes_mask & 0x0f); //set step signals by mask
asm("nop");
PORTC = portC; //set step signals to zero
asm("nop");
#endif //((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3) || (MOTHERBOARD == BOARD_EINSY_1_0a))
}
uint16_t sm4_line_xyze_ui(uint16_t dx, uint16_t dy, uint16_t dz, uint16_t de)
{
uint16_t dd = (uint16_t)(sqrt((float)(((uint32_t)dx)*dx + ((uint32_t)dy*dy) + ((uint32_t)dz*dz) + ((uint32_t)de*de))) + 0.5);
uint16_t nd = dd;
uint16_t cx = dd;
uint16_t cy = dd;
uint16_t cz = dd;
uint16_t ce = dd;
uint16_t x = 0;
uint16_t y = 0;
uint16_t z = 0;
uint16_t e = 0;
while (nd)
{
if (sm4_stop_cb && (*sm4_stop_cb)()) break;
uint8_t sm = 0; //step mask
if (cx <= dx)
{
sm |= 1;
cx += dd;
x++;
}
if (cy <= dy)
{
sm |= 2;
cy += dd;
y++;
}
if (cz <= dz)
{
sm |= 4;
cz += dd;
z++;
}
if (ce <= de)
{
sm |= 4;
ce += dd;
e++;
}
cx -= dx;
cy -= dy;
cz -= dz;
ce -= de;
sm4_do_step(sm);
uint16_t delay = SM4_DEFDELAY;
if (sm4_calc_delay_cb) delay = (*sm4_calc_delay_cb)(nd, dd);
if (delay) delayMicroseconds(delay);
nd--;
}
if (sm4_update_pos_cb) (*sm4_update_pos_cb)(x, y, z, e);
return nd;
}
#endif //NEW_XYZCAL

56
Firmware/sm4.h Normal file
View File

@ -0,0 +1,56 @@
//sm4.h - simple 4-axis stepper control
#ifndef _SM4_H
#define _SM4_H
#include <inttypes.h>
#include "config.h"
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
// callback prototype for stop condition (return 0 - continue, return 1 - stop)
typedef uint8_t (*sm4_stop_cb_t)();
// callback prototype for updating position counters
typedef void (*sm4_update_pos_cb_t)(uint16_t dx, uint16_t dy, uint16_t dz, uint16_t de);
// callback prototype for calculating delay
typedef uint16_t (*sm4_calc_delay_cb_t)(uint16_t nd, uint16_t dd);
// callback pointer - stop
extern sm4_stop_cb_t sm4_stop_cb;
// callback pointer - update_pos
extern sm4_update_pos_cb_t sm4_update_pos_cb;
// callback pointer - calc_delay
extern sm4_calc_delay_cb_t sm4_calc_delay_cb;
// returns direction for single axis (0 - positive, 1 - negative)
extern uint8_t sm4_get_dir(uint8_t axis);
// set direction for single axis (0 - positive, 1 - negative)
extern void sm4_set_dir(uint8_t axis, uint8_t dir);
// returns direction of all axes as bitmask (0 - positive, 1 - negative)
extern uint8_t sm4_get_dir_bits(void);
// set direction for all axes as bitmask (0 - positive, 1 - negative)
extern void sm4_set_dir_bits(uint8_t dir_bits);
// step axes by bitmask
extern void sm4_do_step(uint8_t axes_mask);
// xyze linear-interpolated relative move, returns remaining diagonal steps (>0 means stoped)
extern uint16_t sm4_line_xyze_ui(uint16_t dx, uint16_t dy, uint16_t dz, uint16_t de);
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)
#endif //_SM4_H

194
Firmware/sound.cpp Normal file
View File

@ -0,0 +1,194 @@
#include "sound.h"
#include "Marlin.h"
//#include <inttypes.h>
//#include <avr/eeprom.h>
//#include "eeprom.h"
//eSOUND_MODE eSoundMode=e_SOUND_MODE_LOUD;
// nema vyznam, pokud se bude volat Sound_Init (tzn. poc. hodnota je v EEPROM)
// !?! eSOUND_MODE eSoundMode; v ultraldc.cpp :: cd_settings_menu() se takto jevi jako lokalni promenna
eSOUND_MODE eSoundMode; //=e_SOUND_MODE_DEFAULT;
static void Sound_SaveMode(void);
static void Sound_DoSound_Echo(void);
static void Sound_DoSound_Prompt(void);
static void Sound_DoSound_Alert(bool bOnce);
static void Sound_DoSound_Encoder_Move(void);
static void Sound_DoSound_Blind_Alert(void);
void Sound_Init(void)
{
SET_OUTPUT(BEEPER);
eSoundMode=(eSOUND_MODE)eeprom_read_byte((uint8_t*)EEPROM_SOUND_MODE);
if(eSoundMode==e_SOUND_MODE_NULL)
Sound_Default(); // je potreba provest i ulozeni do EEPROM
}
void Sound_Default(void)
{
eSoundMode=e_SOUND_MODE_DEFAULT;
Sound_SaveMode();
}
void Sound_SaveMode(void)
{
eeprom_update_byte((uint8_t*)EEPROM_SOUND_MODE,(uint8_t)eSoundMode);
}
void Sound_CycleState(void)
{
switch(eSoundMode)
{
case e_SOUND_MODE_LOUD:
eSoundMode=e_SOUND_MODE_ONCE;
break;
case e_SOUND_MODE_ONCE:
eSoundMode=e_SOUND_MODE_SILENT;
break;
case e_SOUND_MODE_SILENT:
eSoundMode=e_SOUND_MODE_BLIND;
break;
case e_SOUND_MODE_BLIND:
eSoundMode=e_SOUND_MODE_LOUD;
break;
default:
eSoundMode=e_SOUND_MODE_LOUD;
}
Sound_SaveMode();
}
//if critical is true then silend and once mode is ignored
void Sound_MakeCustom(uint16_t ms,uint16_t tone_,bool critical){
if (!critical){
if (eSoundMode != e_SOUND_MODE_SILENT){
if(!tone_){
WRITE(BEEPER, HIGH);
_delay(ms);
WRITE(BEEPER, LOW);
}
else{
_tone(BEEPER, tone_);
_delay(ms);
_noTone(BEEPER);
}
}
}
else{
if(!tone_){
WRITE(BEEPER, HIGH);
_delay(ms);
WRITE(BEEPER, LOW);
_delay(ms);
}
else{
_tone(BEEPER, tone_);
_delay(ms);
_noTone(BEEPER);
}
}
}
void Sound_MakeSound(eSOUND_TYPE eSoundType)
{
switch(eSoundMode)
{
case e_SOUND_MODE_LOUD:
if(eSoundType==e_SOUND_TYPE_ButtonEcho)
Sound_DoSound_Echo();
if(eSoundType==e_SOUND_TYPE_StandardPrompt)
Sound_DoSound_Prompt();
if(eSoundType==e_SOUND_TYPE_StandardAlert)
Sound_DoSound_Alert(false);
break;
case e_SOUND_MODE_ONCE:
if(eSoundType==e_SOUND_TYPE_ButtonEcho)
Sound_DoSound_Echo();
if(eSoundType==e_SOUND_TYPE_StandardPrompt)
Sound_DoSound_Prompt();
if(eSoundType==e_SOUND_TYPE_StandardAlert)
Sound_DoSound_Alert(true);
break;
case e_SOUND_MODE_SILENT:
if(eSoundType==e_SOUND_TYPE_StandardAlert)
Sound_DoSound_Alert(true);
break;
case e_SOUND_MODE_BLIND:
if(eSoundType==e_SOUND_TYPE_ButtonEcho)
Sound_DoSound_Echo();
if(eSoundType==e_SOUND_TYPE_StandardPrompt)
Sound_DoSound_Prompt();
if(eSoundType==e_SOUND_TYPE_StandardAlert)
Sound_DoSound_Alert(false);
if(eSoundType==e_SOUND_TYPE_EncoderMove)
Sound_DoSound_Encoder_Move();
if(eSoundType==e_SOUND_TYPE_BlindAlert)
Sound_DoSound_Blind_Alert();
break;
default:
break;
}
}
static void Sound_DoSound_Blind_Alert(void)
{
uint8_t nI;
for(nI=0; nI<20; nI++)
{
WRITE(BEEPER,HIGH);
delayMicroseconds(94);
WRITE(BEEPER,LOW);
delayMicroseconds(94);
}
}
static void Sound_DoSound_Encoder_Move(void)
{
uint8_t nI;
for(nI=0;nI<5;nI++)
{
WRITE(BEEPER,HIGH);
delayMicroseconds(75);
WRITE(BEEPER,LOW);
delayMicroseconds(75);
}
}
static void Sound_DoSound_Echo(void)
{
uint8_t nI;
for(nI=0;nI<10;nI++)
{
WRITE(BEEPER,HIGH);
delayMicroseconds(100);
WRITE(BEEPER,LOW);
delayMicroseconds(100);
}
}
static void Sound_DoSound_Prompt(void)
{
WRITE(BEEPER,HIGH);
_delay_ms(500);
WRITE(BEEPER,LOW);
}
static void Sound_DoSound_Alert(bool bOnce)
{
uint8_t nI,nMax;
nMax=bOnce?1:3;
for(nI=0;nI<nMax;nI++)
{
WRITE(BEEPER,HIGH);
delayMicroseconds(200);
WRITE(BEEPER,LOW);
delayMicroseconds(500);
}
}

36
Firmware/sound.h Normal file
View File

@ -0,0 +1,36 @@
#include <stdint.h>
#ifndef SOUND_H
#define SOUND_H
#define MSG_SOUND_MODE_LOUD "Sound [loud]"
#define MSG_SOUND_MODE_ONCE "Sound [once]"
#define MSG_SOUND_MODE_SILENT "Sound [silent]"
#define MSG_SOUND_MODE_BLIND "Sound [assist]"
#define e_SOUND_MODE_NULL 0xFF
typedef enum
{e_SOUND_MODE_LOUD,e_SOUND_MODE_ONCE,e_SOUND_MODE_SILENT,e_SOUND_MODE_BLIND} eSOUND_MODE;
#define e_SOUND_MODE_DEFAULT e_SOUND_MODE_LOUD
typedef enum
{e_SOUND_TYPE_ButtonEcho,e_SOUND_TYPE_EncoderEcho,e_SOUND_TYPE_StandardPrompt,e_SOUND_TYPE_StandardConfirm,e_SOUND_TYPE_StandardWarning,e_SOUND_TYPE_StandardAlert,e_SOUND_TYPE_EncoderMove,e_SOUND_TYPE_BlindAlert} eSOUND_TYPE;
typedef enum
{e_SOUND_CLASS_Echo,e_SOUND_CLASS_Prompt,e_SOUND_CLASS_Confirm,e_SOUND_CLASS_Warning,e_SOUND_CLASS_Alert} eSOUND_CLASS;
extern eSOUND_MODE eSoundMode;
extern void Sound_Init(void);
extern void Sound_Default(void);
extern void Sound_Save(void);
extern void Sound_CycleState(void);
extern void Sound_MakeSound(eSOUND_TYPE eSoundType);
extern void Sound_MakeCustom(uint16_t ms,uint16_t tone_ ,bool critical);
//static void Sound_DoSound_Echo(void);
//static void Sound_DoSound_Prompt(void);
#endif // SOUND_H

View File

@ -0,0 +1,152 @@
#ifndef SPEED_LOOKUPTABLE_H
#define SPEED_LOOKUPTABLE_H
#include "Marlin.h"
#if F_CPU == 16000000
const uint16_t speed_lookuptable_fast[256][2] PROGMEM = {\
{ 62500, 55556}, { 6944, 3268}, { 3676, 1176}, { 2500, 607}, { 1893, 369}, { 1524, 249}, { 1275, 179}, { 1096, 135},
{ 961, 105}, { 856, 85}, { 771, 69}, { 702, 58}, { 644, 49}, { 595, 42}, { 553, 37}, { 516, 32},
{ 484, 28}, { 456, 25}, { 431, 23}, { 408, 20}, { 388, 19}, { 369, 16}, { 353, 16}, { 337, 14},
{ 323, 13}, { 310, 11}, { 299, 11}, { 288, 11}, { 277, 9}, { 268, 9}, { 259, 8}, { 251, 8},
{ 243, 8}, { 235, 7}, { 228, 6}, { 222, 6}, { 216, 6}, { 210, 6}, { 204, 5}, { 199, 5},
{ 194, 5}, { 189, 4}, { 185, 4}, { 181, 4}, { 177, 4}, { 173, 4}, { 169, 4}, { 165, 3},
{ 162, 3}, { 159, 4}, { 155, 3}, { 152, 3}, { 149, 2}, { 147, 3}, { 144, 3}, { 141, 2},
{ 139, 3}, { 136, 2}, { 134, 2}, { 132, 3}, { 129, 2}, { 127, 2}, { 125, 2}, { 123, 2},
{ 121, 2}, { 119, 1}, { 118, 2}, { 116, 2}, { 114, 1}, { 113, 2}, { 111, 2}, { 109, 1},
{ 108, 2}, { 106, 1}, { 105, 2}, { 103, 1}, { 102, 1}, { 101, 1}, { 100, 2}, { 98, 1},
{ 97, 1}, { 96, 1}, { 95, 2}, { 93, 1}, { 92, 1}, { 91, 1}, { 90, 1}, { 89, 1},
{ 88, 1}, { 87, 1}, { 86, 1}, { 85, 1}, { 84, 1}, { 83, 0}, { 83, 1}, { 82, 1},
{ 81, 1}, { 80, 1}, { 79, 1}, { 78, 0}, { 78, 1}, { 77, 1}, { 76, 1}, { 75, 0},
{ 75, 1}, { 74, 1}, { 73, 1}, { 72, 0}, { 72, 1}, { 71, 1}, { 70, 0}, { 70, 1},
{ 69, 0}, { 69, 1}, { 68, 1}, { 67, 0}, { 67, 1}, { 66, 0}, { 66, 1}, { 65, 0},
{ 65, 1}, { 64, 1}, { 63, 0}, { 63, 1}, { 62, 0}, { 62, 1}, { 61, 0}, { 61, 1},
{ 60, 0}, { 60, 0}, { 60, 1}, { 59, 0}, { 59, 1}, { 58, 0}, { 58, 1}, { 57, 0},
{ 57, 1}, { 56, 0}, { 56, 0}, { 56, 1}, { 55, 0}, { 55, 1}, { 54, 0}, { 54, 0},
{ 54, 1}, { 53, 0}, { 53, 0}, { 53, 1}, { 52, 0}, { 52, 0}, { 52, 1}, { 51, 0},
{ 51, 0}, { 51, 1}, { 50, 0}, { 50, 0}, { 50, 1}, { 49, 0}, { 49, 0}, { 49, 1},
{ 48, 0}, { 48, 0}, { 48, 1}, { 47, 0}, { 47, 0}, { 47, 0}, { 47, 1}, { 46, 0},
{ 46, 0}, { 46, 1}, { 45, 0}, { 45, 0}, { 45, 0}, { 45, 1}, { 44, 0}, { 44, 0},
{ 44, 0}, { 44, 1}, { 43, 0}, { 43, 0}, { 43, 0}, { 43, 1}, { 42, 0}, { 42, 0},
{ 42, 0}, { 42, 1}, { 41, 0}, { 41, 0}, { 41, 0}, { 41, 0}, { 41, 1}, { 40, 0},
{ 40, 0}, { 40, 0}, { 40, 0}, { 40, 1}, { 39, 0}, { 39, 0}, { 39, 0}, { 39, 0},
{ 39, 1}, { 38, 0}, { 38, 0}, { 38, 0}, { 38, 0}, { 38, 1}, { 37, 0}, { 37, 0},
{ 37, 0}, { 37, 0}, { 37, 0}, { 37, 1}, { 36, 0}, { 36, 0}, { 36, 0}, { 36, 0},
{ 36, 1}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 1},
{ 34, 0}, { 34, 0}, { 34, 0}, { 34, 0}, { 34, 0}, { 34, 1}, { 33, 0}, { 33, 0},
{ 33, 0}, { 33, 0}, { 33, 0}, { 33, 0}, { 33, 1}, { 32, 0}, { 32, 0}, { 32, 0},
{ 32, 0}, { 32, 0}, { 32, 0}, { 32, 0}, { 32, 1}, { 31, 0}, { 31, 0}, { 31, 0},
{ 31, 0}, { 31, 0}, { 31, 0}, { 31, 1}, { 30, 0}, { 30, 0}, { 30, 0}, { 30, 0}
};
const uint16_t speed_lookuptable_slow[256][2] PROGMEM = {\
{ 62500, 12500}, { 50000, 8334}, { 41666, 5952}, { 35714, 4464}, { 31250, 3473}, { 27777, 2777}, { 25000, 2273}, { 22727, 1894},
{ 20833, 1603}, { 19230, 1373}, { 17857, 1191}, { 16666, 1041}, { 15625, 920}, { 14705, 817}, { 13888, 731}, { 13157, 657},
{ 12500, 596}, { 11904, 541}, { 11363, 494}, { 10869, 453}, { 10416, 416}, { 10000, 385}, { 9615, 356}, { 9259, 331},
{ 8928, 308}, { 8620, 287}, { 8333, 269}, { 8064, 252}, { 7812, 237}, { 7575, 223}, { 7352, 210}, { 7142, 198},
{ 6944, 188}, { 6756, 178}, { 6578, 168}, { 6410, 160}, { 6250, 153}, { 6097, 145}, { 5952, 139}, { 5813, 132},
{ 5681, 126}, { 5555, 121}, { 5434, 115}, { 5319, 111}, { 5208, 106}, { 5102, 102}, { 5000, 99}, { 4901, 94},
{ 4807, 91}, { 4716, 87}, { 4629, 84}, { 4545, 81}, { 4464, 79}, { 4385, 75}, { 4310, 73}, { 4237, 71},
{ 4166, 68}, { 4098, 66}, { 4032, 64}, { 3968, 62}, { 3906, 60}, { 3846, 59}, { 3787, 56}, { 3731, 55},
{ 3676, 53}, { 3623, 52}, { 3571, 50}, { 3521, 49}, { 3472, 48}, { 3424, 46}, { 3378, 45}, { 3333, 44},
{ 3289, 43}, { 3246, 41}, { 3205, 41}, { 3164, 39}, { 3125, 39}, { 3086, 38}, { 3048, 36}, { 3012, 36},
{ 2976, 35}, { 2941, 35}, { 2906, 33}, { 2873, 33}, { 2840, 32}, { 2808, 31}, { 2777, 30}, { 2747, 30},
{ 2717, 29}, { 2688, 29}, { 2659, 28}, { 2631, 27}, { 2604, 27}, { 2577, 26}, { 2551, 26}, { 2525, 25},
{ 2500, 25}, { 2475, 25}, { 2450, 23}, { 2427, 24}, { 2403, 23}, { 2380, 22}, { 2358, 22}, { 2336, 22},
{ 2314, 21}, { 2293, 21}, { 2272, 20}, { 2252, 20}, { 2232, 20}, { 2212, 20}, { 2192, 19}, { 2173, 18},
{ 2155, 19}, { 2136, 18}, { 2118, 18}, { 2100, 17}, { 2083, 17}, { 2066, 17}, { 2049, 17}, { 2032, 16},
{ 2016, 16}, { 2000, 16}, { 1984, 16}, { 1968, 15}, { 1953, 16}, { 1937, 14}, { 1923, 15}, { 1908, 15},
{ 1893, 14}, { 1879, 14}, { 1865, 14}, { 1851, 13}, { 1838, 14}, { 1824, 13}, { 1811, 13}, { 1798, 13},
{ 1785, 12}, { 1773, 13}, { 1760, 12}, { 1748, 12}, { 1736, 12}, { 1724, 12}, { 1712, 12}, { 1700, 11},
{ 1689, 12}, { 1677, 11}, { 1666, 11}, { 1655, 11}, { 1644, 11}, { 1633, 10}, { 1623, 11}, { 1612, 10},
{ 1602, 10}, { 1592, 10}, { 1582, 10}, { 1572, 10}, { 1562, 10}, { 1552, 9}, { 1543, 10}, { 1533, 9},
{ 1524, 9}, { 1515, 9}, { 1506, 9}, { 1497, 9}, { 1488, 9}, { 1479, 9}, { 1470, 9}, { 1461, 8},
{ 1453, 8}, { 1445, 9}, { 1436, 8}, { 1428, 8}, { 1420, 8}, { 1412, 8}, { 1404, 8}, { 1396, 8},
{ 1388, 7}, { 1381, 8}, { 1373, 7}, { 1366, 8}, { 1358, 7}, { 1351, 7}, { 1344, 8}, { 1336, 7},
{ 1329, 7}, { 1322, 7}, { 1315, 7}, { 1308, 6}, { 1302, 7}, { 1295, 7}, { 1288, 6}, { 1282, 7},
{ 1275, 6}, { 1269, 7}, { 1262, 6}, { 1256, 6}, { 1250, 7}, { 1243, 6}, { 1237, 6}, { 1231, 6},
{ 1225, 6}, { 1219, 6}, { 1213, 6}, { 1207, 6}, { 1201, 5}, { 1196, 6}, { 1190, 6}, { 1184, 5},
{ 1179, 6}, { 1173, 5}, { 1168, 6}, { 1162, 5}, { 1157, 5}, { 1152, 6}, { 1146, 5}, { 1141, 5},
{ 1136, 5}, { 1131, 5}, { 1126, 5}, { 1121, 5}, { 1116, 5}, { 1111, 5}, { 1106, 5}, { 1101, 5},
{ 1096, 5}, { 1091, 5}, { 1086, 4}, { 1082, 5}, { 1077, 5}, { 1072, 4}, { 1068, 5}, { 1063, 4},
{ 1059, 5}, { 1054, 4}, { 1050, 4}, { 1046, 5}, { 1041, 4}, { 1037, 4}, { 1033, 5}, { 1028, 4},
{ 1024, 4}, { 1020, 4}, { 1016, 4}, { 1012, 4}, { 1008, 4}, { 1004, 4}, { 1000, 4}, { 996, 4},
{ 992, 4}, { 988, 4}, { 984, 4}, { 980, 4}, { 976, 4}, { 972, 4}, { 968, 3}, { 965, 3}
};
#elif F_CPU == 20000000
const uint16_t speed_lookuptable_fast[256][2] PROGMEM = {
{62500, 54055}, {8445, 3917}, {4528, 1434}, {3094, 745}, {2349, 456}, {1893, 307}, {1586, 222}, {1364, 167},
{1197, 131}, {1066, 105}, {961, 86}, {875, 72}, {803, 61}, {742, 53}, {689, 45}, {644, 40},
{604, 35}, {569, 32}, {537, 28}, {509, 25}, {484, 23}, {461, 21}, {440, 19}, {421, 17},
{404, 16}, {388, 15}, {373, 14}, {359, 13}, {346, 12}, {334, 11}, {323, 10}, {313, 10},
{303, 9}, {294, 9}, {285, 8}, {277, 7}, {270, 8}, {262, 7}, {255, 6}, {249, 6},
{243, 6}, {237, 6}, {231, 5}, {226, 5}, {221, 5}, {216, 5}, {211, 4}, {207, 5},
{202, 4}, {198, 4}, {194, 4}, {190, 3}, {187, 4}, {183, 3}, {180, 3}, {177, 4},
{173, 3}, {170, 3}, {167, 2}, {165, 3}, {162, 3}, {159, 2}, {157, 3}, {154, 2},
{152, 3}, {149, 2}, {147, 2}, {145, 2}, {143, 2}, {141, 2}, {139, 2}, {137, 2},
{135, 2}, {133, 2}, {131, 2}, {129, 1}, {128, 2}, {126, 2}, {124, 1}, {123, 2},
{121, 1}, {120, 2}, {118, 1}, {117, 1}, {116, 2}, {114, 1}, {113, 1}, {112, 2},
{110, 1}, {109, 1}, {108, 1}, {107, 2}, {105, 1}, {104, 1}, {103, 1}, {102, 1},
{101, 1}, {100, 1}, {99, 1}, {98, 1}, {97, 1}, {96, 1}, {95, 1}, {94, 1},
{93, 1}, {92, 1}, {91, 0}, {91, 1}, {90, 1}, {89, 1}, {88, 1}, {87, 0},
{87, 1}, {86, 1}, {85, 1}, {84, 0}, {84, 1}, {83, 1}, {82, 1}, {81, 0},
{81, 1}, {80, 1}, {79, 0}, {79, 1}, {78, 0}, {78, 1}, {77, 1}, {76, 0},
{76, 1}, {75, 0}, {75, 1}, {74, 1}, {73, 0}, {73, 1}, {72, 0}, {72, 1},
{71, 0}, {71, 1}, {70, 0}, {70, 1}, {69, 0}, {69, 1}, {68, 0}, {68, 1},
{67, 0}, {67, 1}, {66, 0}, {66, 1}, {65, 0}, {65, 0}, {65, 1}, {64, 0},
{64, 1}, {63, 0}, {63, 1}, {62, 0}, {62, 0}, {62, 1}, {61, 0}, {61, 1},
{60, 0}, {60, 0}, {60, 1}, {59, 0}, {59, 0}, {59, 1}, {58, 0}, {58, 0},
{58, 1}, {57, 0}, {57, 0}, {57, 1}, {56, 0}, {56, 0}, {56, 1}, {55, 0},
{55, 0}, {55, 1}, {54, 0}, {54, 0}, {54, 1}, {53, 0}, {53, 0}, {53, 0},
{53, 1}, {52, 0}, {52, 0}, {52, 1}, {51, 0}, {51, 0}, {51, 0}, {51, 1},
{50, 0}, {50, 0}, {50, 0}, {50, 1}, {49, 0}, {49, 0}, {49, 0}, {49, 1},
{48, 0}, {48, 0}, {48, 0}, {48, 1}, {47, 0}, {47, 0}, {47, 0}, {47, 1},
{46, 0}, {46, 0}, {46, 0}, {46, 0}, {46, 1}, {45, 0}, {45, 0}, {45, 0},
{45, 1}, {44, 0}, {44, 0}, {44, 0}, {44, 0}, {44, 1}, {43, 0}, {43, 0},
{43, 0}, {43, 0}, {43, 1}, {42, 0}, {42, 0}, {42, 0}, {42, 0}, {42, 0},
{42, 1}, {41, 0}, {41, 0}, {41, 0}, {41, 0}, {41, 0}, {41, 1}, {40, 0},
{40, 0}, {40, 0}, {40, 0}, {40, 1}, {39, 0}, {39, 0}, {39, 0}, {39, 0},
{39, 0}, {39, 0}, {39, 1}, {38, 0}, {38, 0}, {38, 0}, {38, 0}, {38, 0},
};
const uint16_t speed_lookuptable_slow[256][2] PROGMEM = {
{62500, 10417}, {52083, 7441}, {44642, 5580}, {39062, 4340}, {34722, 3472}, {31250, 2841}, {28409, 2368}, {26041, 2003},
{24038, 1717}, {22321, 1488}, {20833, 1302}, {19531, 1149}, {18382, 1021}, {17361, 914}, {16447, 822}, {15625, 745},
{14880, 676}, {14204, 618}, {13586, 566}, {13020, 520}, {12500, 481}, {12019, 445}, {11574, 414}, {11160, 385},
{10775, 359}, {10416, 336}, {10080, 315}, {9765, 296}, {9469, 278}, {9191, 263}, {8928, 248}, {8680, 235},
{8445, 222}, {8223, 211}, {8012, 200}, {7812, 191}, {7621, 181}, {7440, 173}, {7267, 165}, {7102, 158},
{6944, 151}, {6793, 145}, {6648, 138}, {6510, 133}, {6377, 127}, {6250, 123}, {6127, 118}, {6009, 113},
{5896, 109}, {5787, 106}, {5681, 101}, {5580, 98}, {5482, 95}, {5387, 91}, {5296, 88}, {5208, 86},
{5122, 82}, {5040, 80}, {4960, 78}, {4882, 75}, {4807, 73}, {4734, 70}, {4664, 69}, {4595, 67},
{4528, 64}, {4464, 63}, {4401, 61}, {4340, 60}, {4280, 58}, {4222, 56}, {4166, 55}, {4111, 53},
{4058, 52}, {4006, 51}, {3955, 49}, {3906, 48}, {3858, 48}, {3810, 45}, {3765, 45}, {3720, 44},
{3676, 43}, {3633, 42}, {3591, 40}, {3551, 40}, {3511, 39}, {3472, 38}, {3434, 38}, {3396, 36},
{3360, 36}, {3324, 35}, {3289, 34}, {3255, 34}, {3221, 33}, {3188, 32}, {3156, 31}, {3125, 31},
{3094, 31}, {3063, 30}, {3033, 29}, {3004, 28}, {2976, 28}, {2948, 28}, {2920, 27}, {2893, 27},
{2866, 26}, {2840, 25}, {2815, 25}, {2790, 25}, {2765, 24}, {2741, 24}, {2717, 24}, {2693, 23},
{2670, 22}, {2648, 22}, {2626, 22}, {2604, 22}, {2582, 21}, {2561, 21}, {2540, 20}, {2520, 20},
{2500, 20}, {2480, 20}, {2460, 19}, {2441, 19}, {2422, 19}, {2403, 18}, {2385, 18}, {2367, 18},
{2349, 17}, {2332, 18}, {2314, 17}, {2297, 16}, {2281, 17}, {2264, 16}, {2248, 16}, {2232, 16},
{2216, 16}, {2200, 15}, {2185, 15}, {2170, 15}, {2155, 15}, {2140, 15}, {2125, 14}, {2111, 14},
{2097, 14}, {2083, 14}, {2069, 14}, {2055, 13}, {2042, 13}, {2029, 13}, {2016, 13}, {2003, 13},
{1990, 13}, {1977, 12}, {1965, 12}, {1953, 13}, {1940, 11}, {1929, 12}, {1917, 12}, {1905, 12},
{1893, 11}, {1882, 11}, {1871, 11}, {1860, 11}, {1849, 11}, {1838, 11}, {1827, 11}, {1816, 10},
{1806, 11}, {1795, 10}, {1785, 10}, {1775, 10}, {1765, 10}, {1755, 10}, {1745, 9}, {1736, 10},
{1726, 9}, {1717, 10}, {1707, 9}, {1698, 9}, {1689, 9}, {1680, 9}, {1671, 9}, {1662, 9},
{1653, 9}, {1644, 8}, {1636, 9}, {1627, 8}, {1619, 9}, {1610, 8}, {1602, 8}, {1594, 8},
{1586, 8}, {1578, 8}, {1570, 8}, {1562, 8}, {1554, 7}, {1547, 8}, {1539, 8}, {1531, 7},
{1524, 8}, {1516, 7}, {1509, 7}, {1502, 7}, {1495, 7}, {1488, 7}, {1481, 7}, {1474, 7},
{1467, 7}, {1460, 7}, {1453, 7}, {1446, 6}, {1440, 7}, {1433, 7}, {1426, 6}, {1420, 6},
{1414, 7}, {1407, 6}, {1401, 6}, {1395, 7}, {1388, 6}, {1382, 6}, {1376, 6}, {1370, 6},
{1364, 6}, {1358, 6}, {1352, 6}, {1346, 5}, {1341, 6}, {1335, 6}, {1329, 5}, {1324, 6},
{1318, 5}, {1313, 6}, {1307, 5}, {1302, 6}, {1296, 5}, {1291, 5}, {1286, 6}, {1280, 5},
{1275, 5}, {1270, 5}, {1265, 5}, {1260, 5}, {1255, 5}, {1250, 5}, {1245, 5}, {1240, 5},
{1235, 5}, {1230, 5}, {1225, 5}, {1220, 5}, {1215, 4}, {1211, 5}, {1206, 5}, {1201, 5},
};
#endif
#endif

8
Firmware/spi.c Normal file
View File

@ -0,0 +1,8 @@
//spi.c - hardware SPI
//#ifdef __SPI
#include "spi.h"
#include <avr/io.h>
//#endif //__SPI

49
Firmware/spi.h Normal file
View File

@ -0,0 +1,49 @@
//spi.h - hardware SPI
#ifndef SPI_H
#define SPI_H
#include <inttypes.h>
#include <avr/io.h>
#include "config.h"
#define SPI_SPCR(rat, pha, pol, mst, dor) ((rat & 3) | (pha?(1<<CPHA):0) | (pol?(1<<CPOL):0) | (mst?(1<<MSTR):0) | (dor?(1<<DORD):0) | (1<<SPE))
#define SPI_SPSR(rat) ((rat & 4)?(1<<SPI2X):0)
#define DD_SS 0
#define DD_SCK 1
#define DD_MOSI 2
#define DD_MISO 3
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
static inline void spi_init()
{
DDRB &= ~((1 << DD_SCK) | (1 << DD_MOSI) | (1 << DD_MISO));
DDRB |= (1 << DD_SS) | (1 << DD_SCK) | (1 << DD_MOSI);
PORTB &= ~((1 << DD_SCK) | (1 << DD_MOSI) | (1 << DD_MISO));
PORTB |= (1 << DD_SS);
SPCR = SPI_SPCR(0, 0, 0, 1, 0); //SPE=1, MSTR=1 (0x50)
SPSR = 0x00;
}
static inline void spi_setup(uint8_t spcr, uint8_t spsr)
{
SPCR = spcr;
SPSR = spsr;
}
static inline uint8_t spi_txrx(uint8_t tx)
{
SPDR = tx;
while (!(SPSR & (1 << SPIF)));
return SPDR;
}
#if defined(__cplusplus)
}
#endif //defined(__cplusplus)
#endif //SPI_H

Some files were not shown because too many files have changed in this diff Show More