add og firmware
This commit is contained in:
parent
f3c8b13122
commit
b2495bdfac
|
@ -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
|
|
@ -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; }
|
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
@ -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
|
|
@ -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");
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
|
@ -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
|
||||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>;
|
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||||
|
}
|
|
@ -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_ */
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef OPTIBOOT_W25X20CL_H
|
||||||
|
#define OPTIBOOT_W25X20CL_H
|
||||||
|
|
||||||
|
extern void optiboot_w25x20cl_enter();
|
||||||
|
|
||||||
|
#endif /* OPTIBOOT_W25X20CL_H */
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
||||||
|
//spi.c - hardware SPI
|
||||||
|
//#ifdef __SPI
|
||||||
|
|
||||||
|
#include "spi.h"
|
||||||
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
|
||||||
|
//#endif //__SPI
|
|
@ -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
Loading…
Reference in New Issue