From 35983149e742c8393339f6501cde7f17802b0464 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 22 Oct 2016 16:49:53 +1300 Subject: [PATCH 01/66] new file: makefile new file: src/Segger_SystemView/Global.h new file: src/Segger_SystemView/SEGGER.h new file: src/Segger_SystemView/SEGGER_RTT.c new file: src/Segger_SystemView/SEGGER_RTT.h new file: src/Segger_SystemView/SEGGER_RTT_Conf.h new file: src/Segger_SystemView/SEGGER_SYSVIEW.c new file: src/Segger_SystemView/SEGGER_SYSVIEW.h new file: src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h new file: src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h new file: src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c new file: src/Segger_SystemView/SEGGER_SYSVIEW_Int.h new file: src/config.default.cartesian new file: src/generate-version.bat new file: src/generate-version.sh new file: src/libs/Debug/HardFault_HandlerC.c new file: src/modules/tools/extruder/Extruder.cpp new file: src/modules/tools/extruder/Extruder.h new file: src/modules/tools/extruder/ExtruderMaker.cpp new file: src/modules/tools/extruder/ExtruderMaker.h new file: src/modules/tools/extruder/ExtruderPublicAccess.h new file: src/modules/tools/filamentdetector/FilamentDetector.cpp new file: src/modules/tools/filamentdetector/FilamentDetector.h new file: src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp new file: src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.h new file: src/modules/tools/scaracal/SCARAcal.cpp new file: src/modules/tools/scaracal/SCARAcal.h new file: src/modules/tools/spindle/Spindle.cpp new file: src/modules/tools/spindle/Spindle.h new file: src/modules/tools/temperatureswitch/TemperatureSwitch.cpp new file: src/modules/tools/temperatureswitch/TemperatureSwitch.h new file: src/modules/utils/PlayLed/PlayLed.cpp new file: src/modules/utils/PlayLed/PlayLed.h new file: src/modules/utils/configurator/Configurator.cpp new file: src/modules/utils/configurator/Configurator.h new file: src/modules/utils/currentcontrol/CurrentControl.cpp new file: src/modules/utils/currentcontrol/CurrentControl.h new file: src/modules/utils/currentcontrol/DigipotBase.h new file: src/modules/utils/currentcontrol/ad5206.h new file: src/modules/utils/currentcontrol/mcp4451.h new file: src/modules/utils/killbutton/KillButton.cpp new file: src/modules/utils/killbutton/KillButton.h new file: src/modules/utils/motordrivercontrol/MotorDriverControl.cpp new file: src/modules/utils/motordrivercontrol/MotorDriverControl.h new file: src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.cpp new file: src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.h --- makefile | 35 + src/Segger_SystemView/Global.h | 102 + src/Segger_SystemView/SEGGER.h | 155 + src/Segger_SystemView/SEGGER_RTT.c | 1385 +++++++++ src/Segger_SystemView/SEGGER_RTT.h | 246 ++ src/Segger_SystemView/SEGGER_RTT_Conf.h | 269 ++ src/Segger_SystemView/SEGGER_SYSVIEW.c | 2763 +++++++++++++++++ src/Segger_SystemView/SEGGER_SYSVIEW.h | 334 ++ src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h | 160 + .../SEGGER_SYSVIEW_ConfDefaults.h | 179 ++ .../SEGGER_SYSVIEW_Config_NoOS.c | 115 + src/Segger_SystemView/SEGGER_SYSVIEW_Int.h | 110 + src/config.default.cartesian | 195 ++ src/generate-version.bat | 6 + src/generate-version.sh | 2 + src/libs/Debug/HardFault_HandlerC.c | 83 + src/modules/tools/extruder/Extruder.cpp | 422 +++ src/modules/tools/extruder/Extruder.h | 66 + src/modules/tools/extruder/ExtruderMaker.cpp | 90 + src/modules/tools/extruder/ExtruderMaker.h | 18 + .../tools/extruder/ExtruderPublicAccess.h | 15 + .../filamentdetector/FilamentDetector.cpp | 246 ++ .../tools/filamentdetector/FilamentDetector.h | 48 + .../RotaryDeltaCalibration.cpp | 123 + .../RotaryDeltaCalibration.h | 18 + src/modules/tools/scaracal/SCARAcal.cpp | 291 ++ src/modules/tools/scaracal/SCARAcal.h | 48 + src/modules/tools/spindle/Spindle.cpp | 210 ++ src/modules/tools/spindle/Spindle.h | 60 + .../temperatureswitch/TemperatureSwitch.cpp | 241 ++ .../temperatureswitch/TemperatureSwitch.h | 82 + src/modules/utils/PlayLed/PlayLed.cpp | 60 + src/modules/utils/PlayLed/PlayLed.h | 24 + .../utils/configurator/Configurator.cpp | 132 + src/modules/utils/configurator/Configurator.h | 28 + .../utils/currentcontrol/CurrentControl.cpp | 110 + .../utils/currentcontrol/CurrentControl.h | 25 + .../utils/currentcontrol/DigipotBase.h | 20 + src/modules/utils/currentcontrol/ad5206.h | 62 + src/modules/utils/currentcontrol/mcp4451.h | 71 + src/modules/utils/killbutton/KillButton.cpp | 109 + src/modules/utils/killbutton/KillButton.h | 31 + .../motordrivercontrol/MotorDriverControl.cpp | 522 ++++ .../motordrivercontrol/MotorDriverControl.h | 79 + .../drivers/DRV8711/drv8711.cpp | 459 +++ .../drivers/DRV8711/drv8711.h | 151 + .../drivers/TMC21X/TMC21X.cpp | 1380 ++++++++ .../drivers/TMC21X/TMC21X.h | 455 +++ .../drivers/TMC26X/TMC26X.cpp | 1133 +++++++ .../drivers/TMC26X/TMC26X.h | 453 +++ src/version.cpp | 11 + src/version.h | 8 + 52 files changed, 13440 insertions(+) create mode 100644 makefile create mode 100755 src/Segger_SystemView/Global.h create mode 100755 src/Segger_SystemView/SEGGER.h create mode 100755 src/Segger_SystemView/SEGGER_RTT.c create mode 100755 src/Segger_SystemView/SEGGER_RTT.h create mode 100755 src/Segger_SystemView/SEGGER_RTT_Conf.h create mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW.c create mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW.h create mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h create mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h create mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c create mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_Int.h create mode 100644 src/config.default.cartesian create mode 100644 src/generate-version.bat create mode 100755 src/generate-version.sh create mode 100644 src/libs/Debug/HardFault_HandlerC.c create mode 100644 src/modules/tools/extruder/Extruder.cpp create mode 100644 src/modules/tools/extruder/Extruder.h create mode 100644 src/modules/tools/extruder/ExtruderMaker.cpp create mode 100644 src/modules/tools/extruder/ExtruderMaker.h create mode 100644 src/modules/tools/extruder/ExtruderPublicAccess.h create mode 100644 src/modules/tools/filamentdetector/FilamentDetector.cpp create mode 100644 src/modules/tools/filamentdetector/FilamentDetector.h create mode 100644 src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp create mode 100644 src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.h create mode 100644 src/modules/tools/scaracal/SCARAcal.cpp create mode 100644 src/modules/tools/scaracal/SCARAcal.h create mode 100644 src/modules/tools/spindle/Spindle.cpp create mode 100644 src/modules/tools/spindle/Spindle.h create mode 100755 src/modules/tools/temperatureswitch/TemperatureSwitch.cpp create mode 100755 src/modules/tools/temperatureswitch/TemperatureSwitch.h create mode 100644 src/modules/utils/PlayLed/PlayLed.cpp create mode 100644 src/modules/utils/PlayLed/PlayLed.h create mode 100644 src/modules/utils/configurator/Configurator.cpp create mode 100644 src/modules/utils/configurator/Configurator.h create mode 100644 src/modules/utils/currentcontrol/CurrentControl.cpp create mode 100644 src/modules/utils/currentcontrol/CurrentControl.h create mode 100644 src/modules/utils/currentcontrol/DigipotBase.h create mode 100644 src/modules/utils/currentcontrol/ad5206.h create mode 100644 src/modules/utils/currentcontrol/mcp4451.h create mode 100644 src/modules/utils/killbutton/KillButton.cpp create mode 100644 src/modules/utils/killbutton/KillButton.h create mode 100644 src/modules/utils/motordrivercontrol/MotorDriverControl.cpp create mode 100644 src/modules/utils/motordrivercontrol/MotorDriverControl.h create mode 100644 src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.cpp create mode 100644 src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.h create mode 100644 src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp create mode 100644 src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.h create mode 100644 src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp create mode 100644 src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.h create mode 100644 src/version.cpp create mode 100644 src/version.h diff --git a/makefile b/makefile new file mode 100644 index 00000000..3fbd2885 --- /dev/null +++ b/makefile @@ -0,0 +1,35 @@ + +#DIRS = gcc4mbed src +DIRS = src +DIRSCLEAN = $(addsuffix .clean,$(DIRS)) + +all: +# @ $(MAKE) -C gcc4mbed + @echo Building Smoothie + @ $(MAKE) -C src + +clean: $(DIRSCLEAN) + +$(DIRSCLEAN): %.clean: + @echo Cleaning $* + @ $(MAKE) -C $* clean + +debug-store: + @ $(MAKE) -C src debug-store + +flash: + @ $(MAKE) -C src flash + +dfu: + @ $(MAKE) -C src dfu + +upload: + @ $(MAKE) -C src upload + +debug: + @ $(MAKE) -C src debug + +console: + @ $(MAKE) -C src console + +.PHONY: all $(DIRS) $(DIRSCLEAN) debug-store flash upload debug console dfu diff --git a/src/Segger_SystemView/Global.h b/src/Segger_SystemView/Global.h new file mode 100755 index 00000000..e93acbcb --- /dev/null +++ b/src/Segger_SystemView/Global.h @@ -0,0 +1,102 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +---------------------------------------------------------------------- +File : Global.h +Purpose : Global types + In case your application already has a Global.h, you should + merge the files. In order to use Segger code, the types + U8, U16, U32, I8, I16, I32 need to be defined in Global.h; + additional definitions do not hurt. +---------------------------END-OF-HEADER------------------------------ +*/ + +#ifndef GLOBAL_H // Guard against multiple inclusion +#define GLOBAL_H + +#define U8 unsigned char +#define U16 unsigned short +#define U32 unsigned long +#define I8 signed char +#define I16 signed short +#define I32 signed long + +#ifdef _WIN32 + // + // Microsoft VC6 compiler related + // + #define U64 unsigned __int64 + #define U128 unsigned __int128 + #define I64 __int64 + #define I128 __int128 + #if _MSC_VER <= 1200 + #define U64_C(x) x##UI64 + #else + #define U64_C(x) x##ULL + #endif +#else + // + // C99 compliant compiler + // + #define U64 unsigned long long + #define I64 signed long long + #define U64_C(x) x##ULL +#endif + +#endif // Avoid multiple inclusion + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER.h b/src/Segger_SystemView/SEGGER.h new file mode 100755 index 00000000..7c49a5c7 --- /dev/null +++ b/src/Segger_SystemView/SEGGER.h @@ -0,0 +1,155 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +---------------------------------------------------------------------- +File : SEGGER.h +Purpose : Global types etc & general purpose utility functions +---------------------------END-OF-HEADER------------------------------ +*/ + +#ifndef SEGGER_H // Guard against multiple inclusion +#define SEGGER_H + +#include "Global.h" // Type definitions: U8, U16, U32, I8, I16, I32 + +#if defined(__cplusplus) +extern "C" { /* Make sure we have C-declarations in C++ programs */ +#endif + +/********************************************************************* +* +* Keywords/specifiers +* +********************************************************************** +*/ + +#ifndef INLINE + #ifdef _WIN32 + // + // Microsoft VC6 and newer. + // Force inlining without cost checking. + // + #define INLINE __forceinline + #else + #if (defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) || defined(__RX) || defined(__ICCRX__)) + // + // Other known compilers. + // + #define INLINE inline + #else + // + // Unknown compilers. + // + #define INLINE + #endif + #endif +#endif + +/********************************************************************* +* +* Function-like macros +* +********************************************************************** +*/ + +#define SEGGER_COUNTOF(a) (sizeof((a))/sizeof((a)[0])) +#define SEGGER_MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define SEGGER_MAX(a,b) (((a) > (b)) ? (a) : (b)) + +/********************************************************************* +* +* Types +* +********************************************************************** +*/ + +typedef struct { + char *pBuffer; + int BufferSize; + int Cnt; +} SEGGER_BUFFER_DESC; + +typedef struct { + int CacheLineSize; // 0: No Cache. Most Systems such as ARM9 use a 32 bytes cache line size. + void (*pfDMB) (void); // Optional DMB function for Data Memory Barrier to make sure all memory operations are completed. + void (*pfClean) (void *p, unsigned NumBytes); // Optional clean function for cached memory. + void (*pfInvalidate)(void *p, unsigned NumBytes); // Optional invalidate function for cached memory. +} SEGGER_CACHE_CONFIG; + +/********************************************************************* +* +* Utility functions +* +********************************************************************** +*/ + +void SEGGER_ARM_memcpy (void *pDest, const void *pSrc, int NumBytes); +void SEGGER_memcpy (void *pDest, const void *pSrc, int NumBytes); +void SEGGER_memxor (void *pDest, const void *pSrc, unsigned NumBytes); +void SEGGER_StoreChar (SEGGER_BUFFER_DESC *p, char c); +void SEGGER_PrintUnsigned(SEGGER_BUFFER_DESC *pBufferDesc, U32 v, unsigned Base, int NumDigits); +void SEGGER_PrintInt (SEGGER_BUFFER_DESC *pBufferDesc, I32 v, unsigned Base, unsigned NumDigits); +int SEGGER_snprintf (char *pBuffer, int BufferSize, const char *sFormat, ...); + + +#if defined(__cplusplus) +} /* Make sure we have C-declarations in C++ programs */ +#endif + +#endif // Avoid multiple inclusion + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_RTT.c b/src/Segger_SystemView/SEGGER_RTT.c new file mode 100755 index 00000000..4496d25c --- /dev/null +++ b/src/Segger_SystemView/SEGGER_RTT.c @@ -0,0 +1,1385 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +---------------------------END-OF-HEADER------------------------------ +File : SEGGER_RTT.c +Purpose : Implementation of SEGGER real-time transfer (RTT) which + allows real-time communication on targets which support + debugger memory accesses while the CPU is running. +Revision: $Rev: 3882 $ + +Additional information: + Type "int" is assumed to be 32-bits in size + H->T Host to target communication + T->H Target to host communication + + RTT channel 0 is always present and reserved for Terminal usage. + Name is fixed to "Terminal" + + Effective buffer size: SizeOfBuffer - 1 + + WrOff == RdOff: Buffer is empty + WrOff == (RdOff - 1): Buffer is full + WrOff > RdOff: Free space includes wrap-around + WrOff < RdOff: Used space includes wrap-around + (WrOff == (SizeOfBuffer - 1)) && (RdOff == 0): + Buffer full and wrap-around after next byte + + +---------------------------------------------------------------------- +*/ + +#include "SEGGER_RTT.h" + +#include // for memcpy + +/********************************************************************* +* +* Configuration, default values +* +********************************************************************** +*/ + +#ifndef BUFFER_SIZE_UP + #define BUFFER_SIZE_UP 1024 // Size of the buffer for terminal output of target, up to host +#endif + +#ifndef BUFFER_SIZE_DOWN + #define BUFFER_SIZE_DOWN 16 // Size of the buffer for terminal input to target from host (Usually keyboard input) +#endif + +#ifndef SEGGER_RTT_MAX_NUM_UP_BUFFERS + #define SEGGER_RTT_MAX_NUM_UP_BUFFERS 2 // Number of up-buffers (T->H) available on this target +#endif + +#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS + #define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS 2 // Number of down-buffers (H->T) available on this target +#endif + +#ifndef SEGGER_RTT_BUFFER_SECTION + #if defined(SEGGER_RTT_SECTION) + #define SEGGER_RTT_BUFFER_SECTION SEGGER_RTT_SECTION + #endif +#endif + +#ifndef SEGGER_RTT_ALIGNMENT + #define SEGGER_RTT_ALIGNMENT 0 +#endif + +#ifndef SEGGER_RTT_BUFFER_ALIGNMENT + #define SEGGER_RTT_BUFFER_ALIGNMENT 0 +#endif + +#ifndef SEGGER_RTT_MODE_DEFAULT + #define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP +#endif + +#ifndef SEGGER_RTT_LOCK + #define SEGGER_RTT_LOCK() +#endif + +#ifndef SEGGER_RTT_UNLOCK + #define SEGGER_RTT_UNLOCK() +#endif + +#ifndef STRLEN + #define STRLEN(a) strlen((a)) +#endif + +#ifndef MEMCPY + #define MEMCPY(pDest, pSrc, NumBytes) memcpy((pDest), (pSrc), (NumBytes)) +#endif + +#ifndef MIN + #define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX + #define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +// +// For some environments, NULL may not be defined until certain headers are included +// +#ifndef NULL + #define NULL 0 +#endif + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ +#if (defined __ICCARM__) || (defined __ICCRX__) + #define RTT_PRAGMA(P) _Pragma(#P) +#endif + +#if SEGGER_RTT_ALIGNMENT || SEGGER_RTT_BUFFER_ALIGNMENT + #if (defined __GNUC__) + #define SEGGER_RTT_ALIGN(Var, Alignment) Var __attribute__ ((aligned (Alignment))) + #elif (defined __ICCARM__) || (defined __ICCRX__) + #define PRAGMA(A) _Pragma(#A) +#define SEGGER_RTT_ALIGN(Var, Alignment) RTT_PRAGMA(data_alignment=Alignment) \ + Var + #elif (defined __CC_ARM__) + #define SEGGER_RTT_ALIGN(Var, Alignment) Var __attribute__ ((aligned (Alignment))) + #else + #error "Alignment not supported for this compiler." + #endif +#else + #define SEGGER_RTT_ALIGN(Var, Alignment) Var +#endif + +#if defined(SEGGER_RTT_SECTION) || defined (SEGGER_RTT_BUFFER_SECTION) + #if (defined __GNUC__) + #define SEGGER_RTT_PUT_SECTION(Var, Section) __attribute__ ((section (Section))) Var + #elif (defined __ICCARM__) || (defined __ICCRX__) +#define SEGGER_RTT_PUT_SECTION(Var, Section) RTT_PRAGMA(location=Section) \ + Var + #elif (defined __CC_ARM__) + #define SEGGER_RTT_PUT_SECTION(Var, Section) __attribute__ ((section (Section), zero_init)) Var + #else + #error "Section placement not supported for this compiler." + #endif +#else + #define SEGGER_RTT_PUT_SECTION(Var, Section) Var +#endif + + +#if SEGGER_RTT_ALIGNMENT + #define SEGGER_RTT_CB_ALIGN(Var) SEGGER_RTT_ALIGN(Var, SEGGER_RTT_ALIGNMENT) +#else + #define SEGGER_RTT_CB_ALIGN(Var) Var +#endif + +#if SEGGER_RTT_BUFFER_ALIGNMENT + #define SEGGER_RTT_BUFFER_ALIGN(Var) SEGGER_RTT_ALIGN(Var, SEGGER_RTT_BUFFER_ALIGNMENT) +#else + #define SEGGER_RTT_BUFFER_ALIGN(Var) Var +#endif + + +#if defined(SEGGER_RTT_SECTION) + #define SEGGER_RTT_PUT_CB_SECTION(Var) SEGGER_RTT_PUT_SECTION(Var, SEGGER_RTT_SECTION) +#else + #define SEGGER_RTT_PUT_CB_SECTION(Var) Var +#endif + +#if defined(SEGGER_RTT_BUFFER_SECTION) + #define SEGGER_RTT_PUT_BUFFER_SECTION(Var) SEGGER_RTT_PUT_SECTION(Var, SEGGER_RTT_BUFFER_SECTION) +#else + #define SEGGER_RTT_PUT_BUFFER_SECTION(Var) Var +#endif + +/********************************************************************* +* +* Static const data +* +********************************************************************** +*/ + +static unsigned char _aTerminalId[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +/********************************************************************* +* +* Static data +* +********************************************************************** +*/ +// +// RTT Control Block and allocate buffers for channel 0 +// +SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT)); + +SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acUpBuffer [BUFFER_SIZE_UP])); +SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acDownBuffer[BUFFER_SIZE_DOWN])); + +static char _ActiveTerminal; + +/********************************************************************* +* +* Static functions +* +********************************************************************** +*/ + +/********************************************************************* +* +* _DoInit() +* +* Function description +* Initializes the control block an buffers. +* May only be called via INIT() to avoid overriding settings. +* +*/ +#define INIT() do { \ + if (_SEGGER_RTT.acID[0] == '\0') { _DoInit(); } \ + } while (0) +static void _DoInit(void) { + SEGGER_RTT_CB* p; + // + // Initialize control block + // + p = &_SEGGER_RTT; + p->MaxNumUpBuffers = SEGGER_RTT_MAX_NUM_UP_BUFFERS; + p->MaxNumDownBuffers = SEGGER_RTT_MAX_NUM_DOWN_BUFFERS; + // + // Initialize up buffer 0 + // + p->aUp[0].sName = "Terminal"; + p->aUp[0].pBuffer = _acUpBuffer; + p->aUp[0].SizeOfBuffer = sizeof(_acUpBuffer); + p->aUp[0].RdOff = 0u; + p->aUp[0].WrOff = 0u; + p->aUp[0].Flags = SEGGER_RTT_MODE_DEFAULT; + // + // Initialize down buffer 0 + // + p->aDown[0].sName = "Terminal"; + p->aDown[0].pBuffer = _acDownBuffer; + p->aDown[0].SizeOfBuffer = sizeof(_acDownBuffer); + p->aDown[0].RdOff = 0u; + p->aDown[0].WrOff = 0u; + p->aDown[0].Flags = SEGGER_RTT_MODE_DEFAULT; + // + // Finish initialization of the control block. + // Copy Id string in three steps to make sure "SEGGER RTT" is not found + // in initializer memory (usually flash) by J-Link + // + strcpy(&p->acID[7], "RTT"); + strcpy(&p->acID[0], "SEGGER"); + p->acID[6] = ' '; +} + +/********************************************************************* +* +* _WriteBlocking() +* +* Function description +* Stores a specified number of characters in SEGGER RTT ring buffer +* and updates the associated write pointer which is periodically +* read by the host. +* The caller is responsible for managing the write chunk sizes as +* _WriteBlocking() will block until all data has been posted successfully. +* +* Parameters +* pRing Ring buffer to post to. +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* >= 0 - Number of bytes written into buffer. +*/ +static unsigned _WriteBlocking(SEGGER_RTT_BUFFER_UP* pRing, const char* pBuffer, unsigned NumBytes) { + unsigned NumBytesToWrite; + unsigned NumBytesWritten; + unsigned RdOff; + unsigned WrOff; + // + // Write data to buffer and handle wrap-around if necessary + // + NumBytesWritten = 0u; + WrOff = pRing->WrOff; + do { + RdOff = pRing->RdOff; // May be changed by host (debug probe) in the meantime + if (RdOff > WrOff) { + NumBytesToWrite = RdOff - WrOff - 1u; + } else { + NumBytesToWrite = pRing->SizeOfBuffer - (WrOff - RdOff + 1u); + } + NumBytesToWrite = MIN(NumBytesToWrite, (pRing->SizeOfBuffer - WrOff)); // Number of bytes that can be written until buffer wrap-around + NumBytesToWrite = MIN(NumBytesToWrite, NumBytes); + memcpy(pRing->pBuffer + WrOff, pBuffer, NumBytesToWrite); + NumBytesWritten += NumBytesToWrite; + pBuffer += NumBytesToWrite; + NumBytes -= NumBytesToWrite; + WrOff += NumBytesToWrite; + if (WrOff == pRing->SizeOfBuffer) { + WrOff = 0u; + } + pRing->WrOff = WrOff; + } while (NumBytes); + // + return NumBytesWritten; +} + +/********************************************************************* +* +* _WriteNoCheck() +* +* Function description +* Stores a specified number of characters in SEGGER RTT ring buffer +* and updates the associated write pointer which is periodically +* read by the host. +* It is callers responsibility to make sure data actually fits in buffer. +* +* Parameters +* pRing Ring buffer to post to. +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Notes +* (1) If there might not be enough space in the "Up"-buffer, call _WriteBlocking +*/ +static void _WriteNoCheck(SEGGER_RTT_BUFFER_UP* pRing, const char* pData, unsigned NumBytes) { + unsigned NumBytesAtOnce; + unsigned WrOff; + unsigned Rem; + + WrOff = pRing->WrOff; + Rem = pRing->SizeOfBuffer - WrOff; + if (Rem > NumBytes) { + // + // All data fits before wrap around + // + memcpy(pRing->pBuffer + WrOff, pData, NumBytes); + pRing->WrOff = WrOff + NumBytes; + } else { + // + // We reach the end of the buffer, so need to wrap around + // + NumBytesAtOnce = Rem; + memcpy(pRing->pBuffer + WrOff, pData, NumBytesAtOnce); + NumBytesAtOnce = NumBytes - Rem; + memcpy(pRing->pBuffer, pData + Rem, NumBytesAtOnce); + pRing->WrOff = NumBytesAtOnce; + } +} + +/********************************************************************* +* +* _PostTerminalSwitch() +* +* Function description +* Switch terminal to the given terminal ID. It is the caller's +* responsibility to ensure the terminal ID is correct and there is +* enough space in the buffer for this to complete successfully. +* +* Parameters +* pRing Ring buffer to post to. +* TerminalId Terminal ID to switch to. +*/ +static void _PostTerminalSwitch(SEGGER_RTT_BUFFER_UP* pRing, unsigned char TerminalId) { + char ac[2]; + + ac[0] = 0xFFu; + ac[1] = _aTerminalId[TerminalId]; // Caller made already sure that TerminalId does not exceed our terminal limit + _WriteBlocking(pRing, ac, 2u); +} + +/********************************************************************* +* +* _GetAvailWriteSpace() +* +* Function description +* Returns the number of bytes that can be written to the ring +* buffer without blocking. +* +* Parameters +* pRing Ring buffer to check. +* +* Return value +* Number of bytes that are free in the buffer. +*/ +static unsigned _GetAvailWriteSpace(SEGGER_RTT_BUFFER_UP* pRing) { + unsigned RdOff; + unsigned WrOff; + unsigned r; + // + // Avoid warnings regarding volatile access order. It's not a problem + // in this case, but dampen compiler enthusiasm. + // + RdOff = pRing->RdOff; + WrOff = pRing->WrOff; + if (RdOff <= WrOff) { + r = pRing->SizeOfBuffer - 1u - WrOff + RdOff; + } else { + r = RdOff - WrOff - 1u; + } + return r; +} + +/********************************************************************* +* +* Public code +* +********************************************************************** +*/ +/********************************************************************* +* +* SEGGER_RTT_ReadNoLock() +* +* Function description +* Reads characters from SEGGER real-time-terminal control block +* which have been previously stored by the host. +* Do not lock against interrupts and multiple access. +* +* Parameters +* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. +* BufferSize Size of the target application buffer. +* +* Return value +* Number of bytes that have been read. +*/ +unsigned SEGGER_RTT_ReadNoLock(unsigned BufferIndex, void* pData, unsigned BufferSize) { + unsigned NumBytesRem; + unsigned NumBytesRead; + unsigned RdOff; + unsigned WrOff; + unsigned char* pBuffer; + SEGGER_RTT_BUFFER_DOWN* pRing; + // + INIT(); + pRing = &_SEGGER_RTT.aDown[BufferIndex]; + pBuffer = (unsigned char*)pData; + RdOff = pRing->RdOff; + WrOff = pRing->WrOff; + NumBytesRead = 0u; + // + // Read from current read position to wrap-around of buffer, first + // + if (RdOff > WrOff) { + NumBytesRem = pRing->SizeOfBuffer - RdOff; + NumBytesRem = MIN(NumBytesRem, BufferSize); + memcpy(pBuffer, pRing->pBuffer + RdOff, NumBytesRem); + NumBytesRead += NumBytesRem; + pBuffer += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; + // + // Handle wrap-around of buffer + // + if (RdOff == pRing->SizeOfBuffer) { + RdOff = 0u; + } + } + // + // Read remaining items of buffer + // + NumBytesRem = WrOff - RdOff; + NumBytesRem = MIN(NumBytesRem, BufferSize); + if (NumBytesRem > 0u) { + memcpy(pBuffer, pRing->pBuffer + RdOff, NumBytesRem); + NumBytesRead += NumBytesRem; + pBuffer += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; + } + if (NumBytesRead) { + pRing->RdOff = RdOff; + } + // + return NumBytesRead; +} + +/********************************************************************* +* +* SEGGER_RTT_Read +* +* Function description +* Reads characters from SEGGER real-time-terminal control block +* which have been previously stored by the host. +* +* Parameters +* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. +* BufferSize Size of the target application buffer. +* +* Return value +* Number of bytes that have been read. +*/ +unsigned SEGGER_RTT_Read(unsigned BufferIndex, void* pBuffer, unsigned BufferSize) { + unsigned NumBytesRead; + // + SEGGER_RTT_LOCK(); + // + // Call the non-locking read function + // + NumBytesRead = SEGGER_RTT_ReadNoLock(BufferIndex, pBuffer, BufferSize); + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + // + return NumBytesRead; +} + +/********************************************************************* +* +* SEGGER_RTT_WriteWithOverwriteNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block. +* SEGGER_RTT_WriteWithOverwriteNoLock does not lock the application +* and overwrites data if the data does not fit into the buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, data is overwritten. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +* (3) Do not use SEGGER_RTT_WriteWithOverwriteNoLock if a J-Link +* connection reads RTT data. +*/ +void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + const char* pData; + SEGGER_RTT_BUFFER_UP* pRing; + unsigned Avail; + + pData = (const char *)pBuffer; + // + // Get "to-host" ring buffer and copy some elements into local variables. + // + pRing = &_SEGGER_RTT.aUp[BufferIndex]; + // + // Check if we will overwrite data and need to adjust the RdOff. + // + if (pRing->WrOff == pRing->RdOff) { + Avail = pRing->SizeOfBuffer - 1u; + } else if ( pRing->WrOff < pRing->RdOff) { + Avail = pRing->RdOff - pRing->WrOff - 1u; + } else { + Avail = pRing->RdOff - pRing->WrOff - 1u + pRing->SizeOfBuffer; + } + if (NumBytes > Avail) { + pRing->RdOff += (NumBytes - Avail); + while (pRing->RdOff >= pRing->SizeOfBuffer) { + pRing->RdOff -= pRing->SizeOfBuffer; + } + } + // + // Write all data, no need to check the RdOff, but possibly handle multiple wrap-arounds + // + Avail = pRing->SizeOfBuffer - pRing->WrOff; + do { + if (Avail > NumBytes) { + // + // Last round + // +#if 1 // memcpy() is good for large amounts of data, but the overhead is too big for small amounts. Use a simple byte loop instead. + char* pDst; + pDst = pRing->pBuffer + pRing->WrOff; + pRing->WrOff += NumBytes; + do { + *pDst++ = *pData++; + } while (--NumBytes); +#else + memcpy(pRing->pBuffer + WrOff, pData, NumBytes); + pRing->WrOff += NumBytes; +#endif + break; //Alternatively: NumBytes = 0; + } else { + // + // Wrap-around necessary, write until wrap-around and reset WrOff + // + memcpy(pRing->pBuffer + pRing->WrOff, pData, Avail); + pData += Avail; + pRing->WrOff = 0; + NumBytes -= Avail; + Avail = (pRing->SizeOfBuffer - 1); + } + } while (NumBytes); +} + +/********************************************************************* +* +* SEGGER_RTT_WriteSkipNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block which is then read by the host. +* SEGGER_RTT_WriteSkipNoLock does not lock the application and +* skips all data, if the data does not fit into the buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, all data is dropped. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +*/ +unsigned SEGGER_RTT_WriteSkipNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + const char* pData; + SEGGER_RTT_BUFFER_UP* pRing; + unsigned Avail; + unsigned RdOff; + unsigned WrOff; + unsigned Rem; + + pData = (const char *)pBuffer; + // + // Get "to-host" ring buffer and copy some elements into local variables. + // + pRing = &_SEGGER_RTT.aUp[BufferIndex]; + RdOff = pRing->RdOff; + WrOff = pRing->WrOff; + // + // Handle the most common cases fastest. + // Which is: + // RdOff <= WrOff -> Space until wrap around is free. + // AND + // WrOff + NumBytes < SizeOfBuffer -> No Wrap around necessary. + // + // OR + // + // RdOff > WrOff -> Space until RdOff - 1 is free. + // AND + // WrOff + NumBytes < RdOff -> Data fits into buffer + // + if (RdOff <= WrOff) { + // + // Get space until WrOff will be at wrap around. + // + Avail = pRing->SizeOfBuffer - 1u - WrOff ; + if (Avail >= NumBytes) { +#if 1 // memcpy() is good for large amounts of data, but the overhead is too big for small amounts. Use a simple byte loop instead. + char* pDst; + pDst = pRing->pBuffer + WrOff; + WrOff += NumBytes; + do { + *pDst++ = *pData++; + } while (--NumBytes); + pRing->WrOff = WrOff + NumBytes; +#else + memcpy(pRing->pBuffer + WrOff, pData, NumBytes); + pRing->WrOff = WrOff + NumBytes; +#endif + return 1; + } + // + // If data did not fit into space until wrap around calculate complete space in buffer. + // + Avail += RdOff; + // + // If there is still no space for the whole of this output, don't bother. + // + if (Avail >= NumBytes) { + // + // OK, we have enough space in buffer. Copy in one or 2 chunks + // + Rem = pRing->SizeOfBuffer - WrOff; // Space until end of buffer + if (Rem > NumBytes) { + memcpy(pRing->pBuffer + WrOff, pData, NumBytes); + pRing->WrOff = WrOff + NumBytes; + } else { + // + // We reach the end of the buffer, so need to wrap around + // + memcpy(pRing->pBuffer + WrOff, pData, Rem); + memcpy(pRing->pBuffer, pData + Rem, NumBytes - Rem); + pRing->WrOff = NumBytes - Rem; + } + return 1; + } + } else { + Avail = RdOff - WrOff - 1u; + if (Avail >= NumBytes) { + memcpy(pRing->pBuffer + WrOff, pData, NumBytes); + pRing->WrOff = WrOff + NumBytes; + return 1; + } + } + // + // If we reach this point no data has been written + // + return 0; +} + +/********************************************************************* +* +* SEGGER_RTT_WriteNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block which is then read by the host. +* SEGGER_RTT_WriteNoLock does not lock the application. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, remaining characters of pBuffer are dropped. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +*/ +unsigned SEGGER_RTT_WriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + unsigned Status; + unsigned Avail; + const char* pData; + SEGGER_RTT_BUFFER_UP* pRing; + + pData = (const char *)pBuffer; + // + // Get "to-host" ring buffer. + // + pRing = &_SEGGER_RTT.aUp[BufferIndex]; + // + // How we output depends upon the mode... + // + switch (pRing->Flags) { + case SEGGER_RTT_MODE_NO_BLOCK_SKIP: + // + // If we are in skip mode and there is no space for the whole + // of this output, don't bother. + // + Avail = _GetAvailWriteSpace(pRing); + if (Avail < NumBytes) { + Status = 0u; + } else { + Status = NumBytes; + _WriteNoCheck(pRing, pData, NumBytes); + } + break; + case SEGGER_RTT_MODE_NO_BLOCK_TRIM: + // + // If we are in trim mode, trim to what we can output without blocking. + // + Avail = _GetAvailWriteSpace(pRing); + Status = Avail < NumBytes ? Avail : NumBytes; + _WriteNoCheck(pRing, pData, Status); + break; + case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: + // + // If we are in blocking mode, output everything. + // + Status = _WriteBlocking(pRing, pData, NumBytes); + break; + default: + Status = 0u; + break; + } + // + // Finish up. + // + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_Write +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block which is then read by the host. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, remaining characters of pBuffer are dropped. +*/ +unsigned SEGGER_RTT_Write(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + unsigned Status; + // + INIT(); + SEGGER_RTT_LOCK(); + // + // Call the non-locking write function + // + Status = SEGGER_RTT_WriteNoLock(BufferIndex, pBuffer, NumBytes); + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + // + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_WriteString +* +* Function description +* Stores string in SEGGER RTT control block. +* This data is read by the host. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* s Pointer to string. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, depending on configuration, +* remaining characters may be dropped or RTT module waits until there is more space in the buffer. +* (2) String passed to this function has to be \0 terminated +* (3) \0 termination character is *not* stored in RTT buffer +*/ +unsigned SEGGER_RTT_WriteString(unsigned BufferIndex, const char* s) { + unsigned Len; + + Len = STRLEN(s); + return SEGGER_RTT_Write(BufferIndex, s, Len); +} + +/********************************************************************* +* +* SEGGER_RTT_GetKey +* +* Function description +* Reads one character from the SEGGER RTT buffer. +* Host has previously stored data there. +* +* Return value +* < 0 - No character available (buffer empty). +* >= 0 - Character which has been read. (Possible values: 0 - 255) +* +* Notes +* (1) This function is only specified for accesses to RTT buffer 0. +*/ +int SEGGER_RTT_GetKey(void) { + char c; + int r; + + r = (int)SEGGER_RTT_Read(0u, &c, 1u); + if (r == 1) { + r = (int)(unsigned char)c; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_WaitKey +* +* Function description +* Waits until at least one character is avaible in the SEGGER RTT buffer. +* Once a character is available, it is read and this function returns. +* +* Return value +* >=0 - Character which has been read. +* +* Notes +* (1) This function is only specified for accesses to RTT buffer 0 +* (2) This function is blocking if no character is present in RTT buffer +*/ +int SEGGER_RTT_WaitKey(void) { + int r; + + do { + r = SEGGER_RTT_GetKey(); + } while (r < 0); + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_HasKey +* +* Function description +* Checks if at least one character for reading is available in the SEGGER RTT buffer. +* +* Return value +* == 0 - No characters are available to read. +* == 1 - At least one character is available. +* +* Notes +* (1) This function is only specified for accesses to RTT buffer 0 +*/ +int SEGGER_RTT_HasKey(void) { + unsigned RdOff; + int r; + + INIT(); + RdOff = _SEGGER_RTT.aDown[0].RdOff; + if (RdOff != _SEGGER_RTT.aDown[0].WrOff) { + r = 1; + } else { + r = 0; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_HasData +* +* Function description +* Check if there is data from the host in the given buffer. +* +* Return value: +* ==0: No data +* !=0: Data in buffer +* +*/ +unsigned SEGGER_RTT_HasData(unsigned BufferIndex) { + SEGGER_RTT_BUFFER_DOWN* pRing; + unsigned v; + + pRing = &_SEGGER_RTT.aDown[BufferIndex]; + v = pRing->WrOff; + return v - pRing->RdOff; +} + +/********************************************************************* +* +* SEGGER_RTT_AllocDownBuffer +* +* Function description +* Run-time configuration of the next down-buffer (H->T). +* The next buffer, which is not used yet is configured. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 - O.K. Buffer Index +* < 0 - Error +*/ +int SEGGER_RTT_AllocDownBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int BufferIndex; + + INIT(); + SEGGER_RTT_LOCK(); + BufferIndex = 0; + do { + if (_SEGGER_RTT.aDown[BufferIndex].pBuffer == NULL) { + break; + } + BufferIndex++; + } while (BufferIndex < _SEGGER_RTT.MaxNumDownBuffers); + if (BufferIndex < _SEGGER_RTT.MaxNumDownBuffers) { + _SEGGER_RTT.aDown[BufferIndex].sName = sName; + _SEGGER_RTT.aDown[BufferIndex].pBuffer = (char*)pBuffer; + _SEGGER_RTT.aDown[BufferIndex].SizeOfBuffer = BufferSize; + _SEGGER_RTT.aDown[BufferIndex].RdOff = 0u; + _SEGGER_RTT.aDown[BufferIndex].WrOff = 0u; + _SEGGER_RTT.aDown[BufferIndex].Flags = Flags; + } else { + BufferIndex = -1; + } + SEGGER_RTT_UNLOCK(); + return BufferIndex; +} + +/********************************************************************* +* +* SEGGER_RTT_AllocUpBuffer +* +* Function description +* Run-time configuration of the next up-buffer (T->H). +* The next buffer, which is not used yet is configured. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 - O.K. Buffer Index +* < 0 - Error +*/ +int SEGGER_RTT_AllocUpBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int BufferIndex; + + INIT(); + SEGGER_RTT_LOCK(); + BufferIndex = 0; + do { + if (_SEGGER_RTT.aUp[BufferIndex].pBuffer == NULL) { + break; + } + BufferIndex++; + } while (BufferIndex < _SEGGER_RTT.MaxNumUpBuffers); + if (BufferIndex < _SEGGER_RTT.MaxNumUpBuffers) { + _SEGGER_RTT.aUp[BufferIndex].sName = sName; + _SEGGER_RTT.aUp[BufferIndex].pBuffer = (char*)pBuffer; + _SEGGER_RTT.aUp[BufferIndex].SizeOfBuffer = BufferSize; + _SEGGER_RTT.aUp[BufferIndex].RdOff = 0u; + _SEGGER_RTT.aUp[BufferIndex].WrOff = 0u; + _SEGGER_RTT.aUp[BufferIndex].Flags = Flags; + } else { + BufferIndex = -1; + } + SEGGER_RTT_UNLOCK(); + return BufferIndex; +} + +/********************************************************************* +* +* SEGGER_RTT_ConfigUpBuffer +* +* Function description +* Run-time configuration of a specific up-buffer (T->H). +* Buffer to be configured is specified by index. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* BufferIndex Index of the buffer to configure. +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 - O.K. +* < 0 - Error +*/ +int SEGGER_RTT_ConfigUpBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int r; + + INIT(); + if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumUpBuffers) { + SEGGER_RTT_LOCK(); + if (BufferIndex > 0u) { + _SEGGER_RTT.aUp[BufferIndex].sName = sName; + _SEGGER_RTT.aUp[BufferIndex].pBuffer = (char*)pBuffer; + _SEGGER_RTT.aUp[BufferIndex].SizeOfBuffer = BufferSize; + _SEGGER_RTT.aUp[BufferIndex].RdOff = 0u; + _SEGGER_RTT.aUp[BufferIndex].WrOff = 0u; + } + _SEGGER_RTT.aUp[BufferIndex].Flags = Flags; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_ConfigDownBuffer +* +* Function description +* Run-time configuration of a specific down-buffer (H->T). +* Buffer to be configured is specified by index. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* BufferIndex Index of the buffer to configure. +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 O.K. +* < 0 Error +*/ +int SEGGER_RTT_ConfigDownBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int r; + + INIT(); + if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumDownBuffers) { + SEGGER_RTT_LOCK(); + if (BufferIndex > 0u) { + _SEGGER_RTT.aDown[BufferIndex].sName = sName; + _SEGGER_RTT.aDown[BufferIndex].pBuffer = (char*)pBuffer; + _SEGGER_RTT.aDown[BufferIndex].SizeOfBuffer = BufferSize; + _SEGGER_RTT.aDown[BufferIndex].RdOff = 0u; + _SEGGER_RTT.aDown[BufferIndex].WrOff = 0u; + } + _SEGGER_RTT.aDown[BufferIndex].Flags = Flags; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_SetNameUpBuffer +* +* Function description +* Run-time configuration of a specific up-buffer name (T->H). +* Buffer to be configured is specified by index. +* +* Parameters +* BufferIndex Index of the buffer to renamed. +* sName Pointer to a constant name string. +* +* Return value +* >= 0 O.K. +* < 0 Error +*/ +int SEGGER_RTT_SetNameUpBuffer(unsigned BufferIndex, const char* sName) { + int r; + + INIT(); + if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumUpBuffers) { + SEGGER_RTT_LOCK(); + _SEGGER_RTT.aUp[BufferIndex].sName = sName; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_SetNameDownBuffer +* +* Function description +* Run-time configuration of a specific Down-buffer name (T->H). +* Buffer to be configured is specified by index. +* +* Parameters +* BufferIndex Index of the buffer to renamed. +* sName Pointer to a constant name string. +* +* Return value +* >= 0 O.K. +* < 0 Error +*/ +int SEGGER_RTT_SetNameDownBuffer(unsigned BufferIndex, const char* sName) { + int r; + + INIT(); + if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumDownBuffers) { + SEGGER_RTT_LOCK(); + _SEGGER_RTT.aDown[BufferIndex].sName = sName; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_Init +* +* Function description +* Initializes the RTT Control Block. +* Should be used in RAM targets, at start of the application. +* +*/ +void SEGGER_RTT_Init (void) { + _DoInit(); +} + +/********************************************************************* +* +* SEGGER_RTT_SetTerminal +* +* Function description +* Sets the terminal to be used for output on channel 0. +* +* Parameters +* TerminalId Index of the terminal. +* +* Return value +* >= 0 O.K. +* < 0 Error (e.g. if RTT is configured for non-blocking mode and there was no space in the buffer to set the new terminal Id) +*/ +int SEGGER_RTT_SetTerminal (char TerminalId) { + char ac[2]; + SEGGER_RTT_BUFFER_UP* pRing; + unsigned Avail; + int r; + // + INIT(); + // + r = 0; + ac[0] = 0xFFU; + if ((unsigned char)TerminalId < (unsigned char)sizeof(_aTerminalId)) { // We only support a certain number of channels + ac[1] = _aTerminalId[(unsigned char)TerminalId]; + pRing = &_SEGGER_RTT.aUp[0]; // Buffer 0 is always reserved for terminal I/O, so we can use index 0 here, fixed + SEGGER_RTT_LOCK(); // Lock to make sure that no other task is writing into buffer, while we are and number of free bytes in buffer does not change downwards after checking and before writing + if ((pRing->Flags & SEGGER_RTT_MODE_MASK) == SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL) { + _ActiveTerminal = TerminalId; + _WriteBlocking(pRing, ac, 2u); + } else { // Skipping mode or trim mode? => We cannot trim this command so handling is the same for both modes + Avail = _GetAvailWriteSpace(pRing); + if (Avail >= 2) { + _ActiveTerminal = TerminalId; // Only change active terminal in case of success + _WriteNoCheck(pRing, ac, 2u); + } else { + r = -1; + } + } + SEGGER_RTT_UNLOCK(); + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_TerminalOut +* +* Function description +* Writes a string to the given terminal +* without changing the terminal for channel 0. +* +* Parameters +* TerminalId Index of the terminal. +* s String to be printed on the terminal. +* +* Return value +* >= 0 - Number of bytes written. +* < 0 - Error. +* +*/ +int SEGGER_RTT_TerminalOut (char TerminalId, const char* s) { + int Status; + unsigned FragLen; + unsigned Avail; + SEGGER_RTT_BUFFER_UP* pRing; + // + INIT(); + // + // Validate terminal ID. + // + if (TerminalId < (char)sizeof(_aTerminalId)) { // We only support a certain number of channels + // + // Get "to-host" ring buffer. + // + pRing = &_SEGGER_RTT.aUp[0]; + // + // Need to be able to change terminal, write data, change back. + // Compute the fixed and variable sizes. + // + FragLen = strlen(s); + // + // How we output depends upon the mode... + // + SEGGER_RTT_LOCK(); + Avail = _GetAvailWriteSpace(pRing); + switch (pRing->Flags & SEGGER_RTT_MODE_MASK) { + case SEGGER_RTT_MODE_NO_BLOCK_SKIP: + // + // If we are in skip mode and there is no space for the whole + // of this output, don't bother switching terminals at all. + // + if (Avail < (FragLen + 4u)) { + Status = 0; + } else { + _PostTerminalSwitch(pRing, TerminalId); + Status = (int)_WriteBlocking(pRing, s, FragLen); + _PostTerminalSwitch(pRing, _ActiveTerminal); + } + break; + case SEGGER_RTT_MODE_NO_BLOCK_TRIM: + // + // If we are in trim mode and there is not enough space for everything, + // trim the output but always include the terminal switch. If no room + // for terminal switch, skip that totally. + // + if (Avail < 4u) { + Status = -1; + } else { + _PostTerminalSwitch(pRing, TerminalId); + Status = (int)_WriteBlocking(pRing, s, (FragLen < (Avail - 4u)) ? FragLen : (Avail - 4u)); + _PostTerminalSwitch(pRing, _ActiveTerminal); + } + break; + case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: + // + // If we are in blocking mode, output everything. + // + _PostTerminalSwitch(pRing, TerminalId); + Status = (int)_WriteBlocking(pRing, s, FragLen); + _PostTerminalSwitch(pRing, _ActiveTerminal); + break; + default: + Status = -1; + break; + } + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + } else { + Status = -1; + } + return Status; +} + + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_RTT.h b/src/Segger_SystemView/SEGGER_RTT.h new file mode 100755 index 00000000..25a4fea9 --- /dev/null +++ b/src/Segger_SystemView/SEGGER_RTT.h @@ -0,0 +1,246 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +---------------------------END-OF-HEADER------------------------------ +File : SEGGER_RTT.h +Purpose : Implementation of SEGGER real-time transfer which allows + real-time communication on targets which support debugger + memory accesses while the CPU is running. +Revision: $Rev: 3667 $ +---------------------------------------------------------------------- +*/ + +#ifndef SEGGER_RTT_H +#define SEGGER_RTT_H + +#include "SEGGER_RTT_Conf.h" + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ + +/********************************************************************* +* +* Types +* +********************************************************************** +*/ + +// +// Description for a circular buffer (also called "ring buffer") +// which is used as up-buffer (T->H) +// +typedef struct { + const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" + char* pBuffer; // Pointer to start of buffer + unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. + unsigned WrOff; // Position of next item to be written by either target. + volatile unsigned RdOff; // Position of next item to be read by host. Must be volatile since it may be modified by host. + unsigned Flags; // Contains configuration flags +} SEGGER_RTT_BUFFER_UP; + +// +// Description for a circular buffer (also called "ring buffer") +// which is used as down-buffer (H->T) +// +typedef struct { + const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" + char* pBuffer; // Pointer to start of buffer + unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. + volatile unsigned WrOff; // Position of next item to be written by host. Must be volatile since it may be modified by host. + unsigned RdOff; // Position of next item to be read by target (down-buffer). + unsigned Flags; // Contains configuration flags +} SEGGER_RTT_BUFFER_DOWN; + +// +// RTT control block which describes the number of buffers available +// as well as the configuration for each buffer +// +// +typedef struct { + char acID[16]; // Initialized to "SEGGER RTT" + int MaxNumUpBuffers; // Initialized to SEGGER_RTT_MAX_NUM_UP_BUFFERS (type. 2) + int MaxNumDownBuffers; // Initialized to SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (type. 2) + SEGGER_RTT_BUFFER_UP aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS]; // Up buffers, transferring information up from target via debug probe to host + SEGGER_RTT_BUFFER_DOWN aDown[SEGGER_RTT_MAX_NUM_DOWN_BUFFERS]; // Down buffers, transferring information down from host via debug probe to target +} SEGGER_RTT_CB; + +/********************************************************************* +* +* Global data +* +********************************************************************** +*/ +extern SEGGER_RTT_CB _SEGGER_RTT; + +/********************************************************************* +* +* RTT API functions +* +********************************************************************** +*/ +#ifdef __cplusplus + extern "C" { +#endif +int SEGGER_RTT_AllocDownBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_AllocUpBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_ConfigUpBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_ConfigDownBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_GetKey (void); +unsigned SEGGER_RTT_HasData (unsigned BufferIndex); +int SEGGER_RTT_HasKey (void); +void SEGGER_RTT_Init (void); +unsigned SEGGER_RTT_Read (unsigned BufferIndex, void* pBuffer, unsigned BufferSize); +unsigned SEGGER_RTT_ReadNoLock (unsigned BufferIndex, void* pData, unsigned BufferSize); +int SEGGER_RTT_SetNameDownBuffer(unsigned BufferIndex, const char* sName); +int SEGGER_RTT_SetNameUpBuffer (unsigned BufferIndex, const char* sName); +int SEGGER_RTT_WaitKey (void); +unsigned SEGGER_RTT_Write (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteSkipNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteString (unsigned BufferIndex, const char* s); +void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +// +// Function macro for performance optimization +// +#define SEGGER_RTT_HASDATA(n) (_SEGGER_RTT.aDown[n].WrOff - _SEGGER_RTT.aDown[n].RdOff) + +/********************************************************************* +* +* RTT "Terminal" API functions +* +********************************************************************** +*/ +int SEGGER_RTT_SetTerminal (char TerminalId); +int SEGGER_RTT_TerminalOut (char TerminalId, const char* s); + +/********************************************************************* +* +* RTT printf functions (require SEGGER_RTT_printf.c) +* +********************************************************************** +*/ +int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...); +#ifdef __cplusplus + } +#endif + +/********************************************************************* +* +* Defines +* +********************************************************************** +*/ + +// +// Operating modes. Define behavior if buffer is full (not enough space for entire message) +// +#define SEGGER_RTT_MODE_NO_BLOCK_SKIP (0U) // Skip. Do not block, output nothing. (Default) +#define SEGGER_RTT_MODE_NO_BLOCK_TRIM (1U) // Trim: Do not block, output as much as fits. +#define SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL (2U) // Block: Wait until there is space in the buffer. +#define SEGGER_RTT_MODE_MASK (3U) + +// +// Control sequences, based on ANSI. +// Can be used to control color, and clear the screen +// +#define RTT_CTRL_RESET "" // Reset to default colors +#define RTT_CTRL_CLEAR "" // Clear screen, reposition cursor to top left + +#define RTT_CTRL_TEXT_BLACK "" +#define RTT_CTRL_TEXT_RED "" +#define RTT_CTRL_TEXT_GREEN "" +#define RTT_CTRL_TEXT_YELLOW "" +#define RTT_CTRL_TEXT_BLUE "" +#define RTT_CTRL_TEXT_MAGENTA "" +#define RTT_CTRL_TEXT_CYAN "" +#define RTT_CTRL_TEXT_WHITE "" + +#define RTT_CTRL_TEXT_BRIGHT_BLACK "" +#define RTT_CTRL_TEXT_BRIGHT_RED "" +#define RTT_CTRL_TEXT_BRIGHT_GREEN "" +#define RTT_CTRL_TEXT_BRIGHT_YELLOW "" +#define RTT_CTRL_TEXT_BRIGHT_BLUE "" +#define RTT_CTRL_TEXT_BRIGHT_MAGENTA "" +#define RTT_CTRL_TEXT_BRIGHT_CYAN "" +#define RTT_CTRL_TEXT_BRIGHT_WHITE "" + +#define RTT_CTRL_BG_BLACK "" +#define RTT_CTRL_BG_RED "" +#define RTT_CTRL_BG_GREEN "" +#define RTT_CTRL_BG_YELLOW "" +#define RTT_CTRL_BG_BLUE "" +#define RTT_CTRL_BG_MAGENTA "" +#define RTT_CTRL_BG_CYAN "" +#define RTT_CTRL_BG_WHITE "" + +#define RTT_CTRL_BG_BRIGHT_BLACK "" +#define RTT_CTRL_BG_BRIGHT_RED "" +#define RTT_CTRL_BG_BRIGHT_GREEN "" +#define RTT_CTRL_BG_BRIGHT_YELLOW "" +#define RTT_CTRL_BG_BRIGHT_BLUE "" +#define RTT_CTRL_BG_BRIGHT_MAGENTA "" +#define RTT_CTRL_BG_BRIGHT_CYAN "" +#define RTT_CTRL_BG_BRIGHT_WHITE "" + + +#endif + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_RTT_Conf.h b/src/Segger_SystemView/SEGGER_RTT_Conf.h new file mode 100755 index 00000000..10e6e0fb --- /dev/null +++ b/src/Segger_SystemView/SEGGER_RTT_Conf.h @@ -0,0 +1,269 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +---------------------------------------------------------------------- +File : SEGGER_RTT_Conf.h +Purpose : Implementation of SEGGER real-time transfer (RTT) which + allows real-time communication on targets which support + debugger memory accesses while the CPU is running. +Revision: $Rev: 3892 $ +---------------------------END-OF-HEADER------------------------------ +*/ + +#ifndef SEGGER_RTT_CONF_H +#define SEGGER_RTT_CONF_H + +#ifdef __IAR_SYSTEMS_ICC__ + #include +#endif + +/********************************************************************* +* +* Defines, configurable +* +********************************************************************** +*/ + +#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (3) // Max. number of up-buffers (T->H) available on this target (Default: 3) +#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (3) // Max. number of down-buffers (H->T) available on this target (Default: 3) + +#define BUFFER_SIZE_UP (1024) // Size of the buffer for terminal output of target, up to host (Default: 1k) +#define BUFFER_SIZE_DOWN (16) // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16) + +#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64) + +#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP // Mode for pre-initialized terminal channel (buffer 0) + +// +// Target is not allowed to perform other RTT operations while string still has not been stored completely. +// Otherwise we would probably end up with a mixed string in the buffer. +// If using RTT from within interrupts, multiple tasks or multi processors, define the SEGGER_RTT_LOCK() and SEGGER_RTT_UNLOCK() function here. +// +// SEGGER_RTT_MAX_INTERRUPT_PRIORITY can be used in the sample lock routines on Cortex-M3/4. +// Make sure to mask all interrupts which can send RTT data, i.e. generate SystemView events, or cause task switches. +// When high-priority interrupts must not be masked while sending RTT data, SEGGER_RTT_MAX_INTERRUPT_PRIORITY needs to be adjusted accordingly. +// (Higher priority = lower priority number) +// Default value for embOS: 128u +// Default configuration in FreeRTOS: configMAX_SYSCALL_INTERRUPT_PRIORITY: ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) +// In case of doubt mask all interrupts: 1 << (8 - BASEPRI_PRIO_BITS) i.e. 1 << 5 when 3 bits are implemented in NVIC +// or define SEGGER_RTT_LOCK() to completely disable interrupts. +// + +#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) // Interrupt priority to lock on SEGGER_RTT_LOCK on Cortex-M3/4 (Default: 0x20) + +/********************************************************************* +* +* RTT lock configuration for SEGGER Embedded Studio, +* Rowley CrossStudio and GCC +*/ +#if (defined __SES_ARM) || (defined __CROSSWORKS_ARM) || (defined __GNUC__) + #ifdef __ARM_ARCH_6M__ + #define SEGGER_RTT_LOCK() { \ + unsigned int LockState; \ + __asm volatile ("mrs %0, primask \n\t" \ + "mov r1, $1 \n\t" \ + "msr primask, r1 \n\t" \ + : "=r" (LockState) \ + : \ + : "r1" \ + ); + + #define SEGGER_RTT_UNLOCK() __asm volatile ("msr primask, %0 \n\t" \ + : \ + : "r" (LockState) \ + : \ + ); \ + } + + #elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)) + #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY + #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) + #endif + #define SEGGER_RTT_LOCK() { \ + unsigned int LockState; \ + __asm volatile ("mrs %0, basepri \n\t" \ + "mov r1, %1 \n\t" \ + "msr basepri, r1 \n\t" \ + : "=r" (LockState) \ + : "i"(SEGGER_RTT_MAX_INTERRUPT_PRIORITY) \ + : "r1" \ + ); + + #define SEGGER_RTT_UNLOCK() __asm volatile ("msr basepri, %0 \n\t" \ + : \ + : "r" (LockState) \ + : \ + ); \ + } + + #elif defined(__ARM_ARCH_7A__) + #define SEGGER_RTT_LOCK() { \ + unsigned int LockState; \ + __asm volatile ("mrs r1, CPSR \n\t" \ + "mov %0, r1 \n\t" \ + "orr r1, r1, #0xC0 \n\t" \ + "msr CPSR_c, r1 \n\t" \ + : "=r" (LockState) \ + : \ + : "r1" \ + ); + + #define SEGGER_RTT_UNLOCK() __asm volatile ("mov r0, %0 \n\t" \ + "mrs r1, CPSR \n\t" \ + "bic r1, r1, #0xC0 \n\t" \ + "and r0, r0, #0xC0 \n\t" \ + "orr r1, r1, r0 \n\t" \ + "msr CPSR_c, r1 \n\t" \ + : \ + : "r" (LockState) \ + : "r0", "r1" \ + ); \ + } +#else + #define SEGGER_RTT_LOCK() + #define SEGGER_RTT_UNLOCK() + #endif +#endif + +/********************************************************************* +* +* RTT lock configuration for IAR EWARM +*/ +#ifdef __ICCARM__ + #if (defined (__ARM6M__) && (__CORE__ == __ARM6M__)) + #define SEGGER_RTT_LOCK() { \ + unsigned int LockState; \ + LockState = __get_PRIMASK(); \ + __set_PRIMASK(1); + + #define SEGGER_RTT_UNLOCK() __set_PRIMASK(LockState); \ + } + #elif ((defined (__ARM7EM__) && (__CORE__ == __ARM7EM__)) || (defined (__ARM7M__) && (__CORE__ == __ARM7M__))) + #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY + #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) + #endif + #define SEGGER_RTT_LOCK() { \ + unsigned int LockState; \ + LockState = __get_BASEPRI(); \ + __set_BASEPRI(SEGGER_RTT_MAX_INTERRUPT_PRIORITY); + + #define SEGGER_RTT_UNLOCK() __set_BASEPRI(LockState); \ + } + #endif +#endif + +/********************************************************************* +* +* RTT lock configuration for IAR RX +*/ +#ifdef __ICCRX__ + #define SEGGER_RTT_LOCK() { \ + unsigned long LockState; \ + LockState = __get_interrupt_state(); \ + __disable_interrupt(); + + #define SEGGER_RTT_UNLOCK() __set_interrupt_state(LockState); \ + } +#endif + +/********************************************************************* +* +* RTT lock configuration for KEIL ARM +*/ +#ifdef __CC_ARM + #if (defined __TARGET_ARCH_6S_M) + #define SEGGER_RTT_LOCK() { \ + unsigned int LockState; \ + register unsigned char PRIMASK __asm( "primask"); \ + LockState = PRIMASK; \ + PRIMASK = 1u; \ + __schedule_barrier(); + + #define SEGGER_RTT_UNLOCK() PRIMASK = LockState; \ + __schedule_barrier(); \ + } + #elif (defined(__TARGET_ARCH_7_M) || defined(__TARGET_ARCH_7E_M)) + #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY + #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) + #endif + #define SEGGER_RTT_LOCK() { \ + unsigned int LockState; \ + register unsigned char BASEPRI __asm( "basepri"); \ + LockState = BASEPRI; \ + BASEPRI = SEGGER_RTT_MAX_INTERRUPT_PRIORITY; \ + __schedule_barrier(); + + #define SEGGER_RTT_UNLOCK() BASEPRI = LockState; \ + __schedule_barrier(); \ + } + #endif +#endif + +/********************************************************************* +* +* RTT lock configuration fallback +*/ +#ifndef SEGGER_RTT_LOCK + #define SEGGER_RTT_LOCK() // Lock RTT (nestable) (i.e. disable interrupts) +#endif + +#ifndef SEGGER_RTT_UNLOCK + #define SEGGER_RTT_UNLOCK() // Unlock RTT (nestable) (i.e. enable previous interrupt lock state) +#endif + +#endif +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW.c b/src/Segger_SystemView/SEGGER_SYSVIEW.c new file mode 100755 index 00000000..87869e7e --- /dev/null +++ b/src/Segger_SystemView/SEGGER_SYSVIEW.c @@ -0,0 +1,2763 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +-------------------------- END-OF-HEADER ----------------------------- + +File : SEGGER_SYSVIEW.c +Purpose : System visualization API implementation. +Revision: $Rev: 4039 $ + +Additional information: + Packet format: + Packets with IDs 0..23 are standard packets with known structure. + For efficiency, they do *NOT* contain a length field. + + + Packets with IDs 24..31 are standard packets with extendible + structure and contain a length field. + + + Packets with IDs >= 32 always contain a length field. + + + Packet IDs: + 0.. 31 : Standard packets, known by SystemViewer. + 32..1023 : OS-definable packets, described in a SystemView description file. + 1024..2047 : User-definable packets, described in a SystemView description file. + 2048..32767: Undefined. + + Data encoding: + Basic types (int, short, char, ...): + Basic types are encoded little endian with most-significant bit variant + encoding. + Each encoded byte contains 7 data bits [6:0] and the MSB continuation bit. + The continuation bit indicates whether the next byte belongs to the data + (bit set) or this is the last byte (bit clear). + The most significant bits of data are encoded first, proceeding to the + least significant bits in the final byte (little endian). + + Example encoding: + Data: 0x1F4 (500) + Encoded: 0xF4 (First 7 data bits 74 | Continuation bit) + 0x03 (Second 7 data bits 03, no continuation) + + Data: 0xFFFFFFFF + Encoded: 0xFF 0xFF 0xFF 0xFF 0x0F + + Data: 0xA2 (162), 0x03 (3), 0x7000 + Encoded: 0xA2 0x01 0x03 0x80 0xE0 0x01 + + Byte arrays and strings: + Byte arrays and strings are encoded as followed by the raw data. + NumBytes is encoded as a basic type with a theoretical maximum of 4G. + + Example encoding: + Data: "Hello World\0" (0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x00) + Encoded: 0x0B 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 + + Examples packets: + 01 F4 03 80 80 10 // Overflow packet. Data is a single U32. + This packet means: 500 packets lost, Timestamp is 0x40000 + + 02 0F 50 // ISR(15) Enter. Timestamp 80 (0x50) + + 03 20 // ISR Exit. Timestamp 32 (0x20) (Shortest possible packet.) + + Sample code for user defined Packets: + #define MY_ID 0x400 // Any value between 0x400 and 0x7FF + void SendMyPacket(unsigned Para0, unsigned Para1, const char* s) { + U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + MAX_STR_LEN + 1]; + U8* pPayload; + // + pPayload = SEGGER_SYSVIEW_PPREPARE_PACKET(aPacket); // Prepare the packet for SystemView + pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para0); // Add the first parameter to the packet + pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para1); // Add the second parameter to the packet + pPayload = SEGGER_SYSVIEW_EncodeString(pPayload, s, MAX_STR_LEN); // Add the string to the packet + // + SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, MY_ID); // Send the packet with EventId = MY_ID + } + + #define MY_ID_1 0x401 + void SendOnePara(unsigned Para0) { + SEGGER_SYSVIEW_RecordU32(MY_ID_1, Para0); + } + +*/ + +/********************************************************************* +* +* #include section +* +********************************************************************** +*/ + +#include "SEGGER_SYSVIEW_Int.h" +#include "SEGGER_RTT.h" +#include +#include +#include + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ +#if SEGGER_SYSVIEW_ID_SHIFT + #define SHRINK_ID(Id) (((Id) - _SYSVIEW_Globals.RAMBaseAddress) >> SEGGER_SYSVIEW_ID_SHIFT) +#else + #define SHRINK_ID(Id) ((Id) - _SYSVIEW_Globals.RAMBaseAddress) +#endif + +#if SEGGER_SYSVIEW_RTT_CHANNEL > 0 + #define CHANNEL_ID_UP SEGGER_SYSVIEW_RTT_CHANNEL + #define CHANNEL_ID_DOWN SEGGER_SYSVIEW_RTT_CHANNEL +#else + #define CHANNEL_ID_UP _SYSVIEW_Globals.UpChannel + #define CHANNEL_ID_DOWN _SYSVIEW_Globals.DownChannel +#endif + +/********************************************************************* +* +* Defines, configurable +* +********************************************************************** +*/ +// Timestamps may be less than full 32-bits, in which case we need to zero +// the unused bits to properly handle overflows. +// Note that this is a quite common scenario, as a 32-bit time such as +// SysTick might be scaled down to reduce bandwith +// or a 16-bit hardware time might be used. +#if SEGGER_SYSVIEW_TIMESTAMP_BITS < 32 // Eliminate unused bits in case hardware timestamps are less than 32 bits + #define MAKE_DELTA_32BIT(Delta) Delta <<= 32 - SEGGER_SYSVIEW_TIMESTAMP_BITS; \ + Delta >>= 32 - SEGGER_SYSVIEW_TIMESTAMP_BITS; +#else + #define MAKE_DELTA_32BIT(Delta) +#endif + + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ +#define ENABLE_STATE_OFF 0 +#define ENABLE_STATE_ON 1 +#define ENABLE_STATE_DROPPING 2 + +#define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0) +#define FORMAT_FLAG_PAD_ZERO (1u << 1) +#define FORMAT_FLAG_PRINT_SIGN (1u << 2) +#define FORMAT_FLAG_ALTERNATE (1u << 3) + +#define MODULE_EVENT_OFFSET (512) + +/********************************************************************* +* +* Types, local +* +********************************************************************** +*/ +typedef struct { + U8* pBuffer; + U8* pPayload; + U8* pPayloadStart; + U32 Options; + unsigned Cnt; +} SEGGER_SYSVIEW_PRINTF_DESC; + +typedef struct { + U8 EnableState; // 0: Disabled, 1: Enabled, (2: Dropping) + U8 UpChannel; + U8 RecursionCnt; + U32 SysFreq; + U32 CPUFreq; + U32 LastTxTimeStamp; + U32 RAMBaseAddress; +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) + U32 PacketCount; +#else + U32 DropCount; + U8 DownChannel; +#endif + U32 DisabledEvents; + const SEGGER_SYSVIEW_OS_API* pOSAPI; + SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC* pfSendSysDesc; +} SEGGER_SYSVIEW_GLOBALS; + +/********************************************************************* +* +* Function prototypes, required +* +********************************************************************** +*/ +static void _SendPacket(U8* pStartPacket, U8* pEndPacket, unsigned EventId); + +/********************************************************************* +* +* Static data +* +********************************************************************** +*/ +static const U8 _abSync[10] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +#ifdef SEGGER_SYSVIEW_SECTION + #if (defined __GNUC__) + __attribute__ ((section (SEGGER_SYSVIEW_SECTION))) static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; + #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) + __attribute__ ((section (SEGGER_SYSVIEW_SECTION))) static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms + #endif + #elif (defined __ICCARM__) || (defined __ICCRX__) + #pragma location=SEGGER_SYSVIEW_SECTION + static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; + #pragma location=SEGGER_SYSVIEW_SECTION + static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms + #elif (defined __CC_ARM__) + __attribute__ ((section (SEGGER_SYSVIEW_SECTION), zero_init)) static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; + #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) + __attribute__ ((section (SEGGER_SYSVIEW_SECTION), zero_init)) static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms + #endif + #else + static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; + #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) + static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms + #endif + #endif +#else + static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; + #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) + static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms + #endif +#endif + +static SEGGER_SYSVIEW_GLOBALS _SYSVIEW_Globals; + +static SEGGER_SYSVIEW_MODULE* _pFirstModule; +static U8 _NumModules; + +/********************************************************************* +* +* Static code +* +********************************************************************** +*/ + +#define ENCODE_U32(pDest, Value) { \ + U8* pSysviewPointer; \ + U32 SysViewData; \ + pSysviewPointer = pDest; \ + SysViewData = Value; \ + while(SysViewData > 0x7F) { \ + *pSysviewPointer++ = (U8)(SysViewData | 0x80); \ + SysViewData >>= 7; \ + }; \ + *pSysviewPointer++ = (U8)SysViewData; \ + pDest = pSysviewPointer; \ + }; + + + +#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 1) +static U8 _aPacket[SEGGER_SYSVIEW_MAX_PACKET_SIZE]; + +#define RECORD_START(PacketSize) SEGGER_SYSVIEW_LOCK(); \ + pPayloadStart = _PreparePacket(_aPacket); + +#define RECORD_END() SEGGER_SYSVIEW_UNLOCK() + +#else + +#define RECORD_START(PacketSize) U8 aPacket[(PacketSize)]; \ + pPayloadStart = _PreparePacket(aPacket); \ + +#define RECORD_END() + +#endif + +/********************************************************************* +* +* _EncodeData() +* +* Function description +* Encode a byte buffer in variable-length format. +* +* Parameters +* pPayload - Pointer to where string will be encoded. +* pSrc - Pointer to data buffer to be encoded. +* NumBytes - Number of bytes in the buffer to be encoded. +* +* Return value +* Pointer to the byte following the value, i.e. the first free +* byte in the payload and the next position to store payload +* content. +* +* Additional information +* The data is encoded as a count byte followed by the contents +* of the data buffer. +* Make sure NumBytes + 1 bytes are free for the payload. +*/ +static U8* _EncodeData(U8* pPayload, const char* pSrc, unsigned NumBytes) { + unsigned n; + // + n = 0; + *pPayload++ = NumBytes; + while (n < NumBytes) { + *pPayload++ = *pSrc++; + n++; + } + return pPayload; +} + +/********************************************************************* +* +* _EncodeStr() +* +* Function description +* Encode a string in variable-length format. +* +* Parameters +* pPayload - Pointer to where string will be encoded. +* pText - String to encode. +* Limit - Maximum number of characters to encode from string. +* +* Return value +* Pointer to the byte following the value, i.e. the first free +* byte in the payload and the next position to store payload +* content. +* +* Additional information +* The string is encoded as a count byte followed by the contents +* of the string. +* No more than 1 + Limit bytes will be encoded to the payload. +*/ +static U8 *_EncodeStr(U8 *pPayload, const char *pText, unsigned Limit) { + unsigned n; + unsigned Len; + // + // Compute string len + // + Len = 0; + while(*(pText + Len) != 0) { + Len++; + } + if (Len > Limit) { + Len = Limit; + } + // + // Write Len + // + if (Len < 255) { + *pPayload++ = Len; + } else { + *pPayload++ = 255; + *pPayload++ = (Len & 255); + *pPayload++ = ((Len >> 8) & 255); + } + // + // copy string + // + n = 0; + while (n < Len) { + *pPayload++ = *pText++; + n++; + } + return pPayload; +} + +/********************************************************************* +* +* _PreparePacket() +* +* Function description +* Prepare a SystemView event packet header. +* +* Parameters +* pPacket - Pointer to start of packet to initialize. +* +* Return value +* Pointer to first byte of packet payload. +* +* Additional information +* The payload length and evnetId are not initialized. +* PreparePacket only reserves space for them and they are +* computed and filled in by the sending function. +*/ +static U8* _PreparePacket(U8* pPacket) { + return pPacket + 4; +} + +/********************************************************************* +* +* _HandleIncomingPacket() +* +* Function description +* Read an incoming command from the down channel and process it. +* +* Additional information +* This function is called each time after sending a packet. +* Processing incoming packets is done asynchronous. SystemView might +* already have sent event packets after the host has sent a command. +*/ +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) +static void _HandleIncomingPacket(void) { + U8 Cmd; + int Status; + // + Status = SEGGER_RTT_ReadNoLock(CHANNEL_ID_DOWN, &Cmd, 1); + if (Status > 0) { + switch (Cmd) { + case SEGGER_SYSVIEW_COMMAND_ID_START: + SEGGER_SYSVIEW_Start(); + break; + case SEGGER_SYSVIEW_COMMAND_ID_STOP: + SEGGER_SYSVIEW_Stop(); + break; + case SEGGER_SYSVIEW_COMMAND_ID_GET_SYSTIME: + SEGGER_SYSVIEW_RecordSystime(); + break; + case SEGGER_SYSVIEW_COMMAND_ID_GET_TASKLIST: + SEGGER_SYSVIEW_SendTaskList(); + break; + case SEGGER_SYSVIEW_COMMAND_ID_GET_SYSDESC: + SEGGER_SYSVIEW_GetSysDesc(); + break; + case SEGGER_SYSVIEW_COMMAND_ID_GET_NUMMODULES: + SEGGER_SYSVIEW_SendNumModules(); + break; + case SEGGER_SYSVIEW_COMMAND_ID_GET_MODULEDESC: + SEGGER_SYSVIEW_SendModuleDescription(); + break; + case SEGGER_SYSVIEW_COMMAND_ID_GET_MODULE: + Status = SEGGER_RTT_ReadNoLock(CHANNEL_ID_DOWN, &Cmd, 1); + if (Status > 0) { + SEGGER_SYSVIEW_SendModule(Cmd); + } + break; + default: + if (Cmd >= 128) { // Unknown extended command. Dummy read its parameter. + SEGGER_RTT_ReadNoLock(CHANNEL_ID_DOWN, &Cmd, 1); + } + break; + } + } +} +#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) + +/********************************************************************* +* +* _TrySendOverflowPacket() +* +* Function description +* Try to transmit an SystemView Overflow packet containing the +* number of dropped packets. +* +* Additional information +* Format as follows: +* 01 Max. packet len is 1 + 5 + 5 = 11 +* +* Example packets sent +* 01 20 40 +* +* Return value +* !=0: Success, Message sent (stored in RTT-Buffer) +* ==0: Buffer full, Message *NOT* stored +* +*/ +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) +static int _TrySendOverflowPacket(void) { + U32 TimeStamp; + I32 Delta; + int Status; + U8 aPacket[11]; + U8* pPayload; + + aPacket[0] = SYSVIEW_EVTID_OVERFLOW; // 1 + pPayload = &aPacket[1]; + ENCODE_U32(pPayload, _SYSVIEW_Globals.DropCount); + // + // Compute time stamp delta and append it to packet. + // + TimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); + Delta = TimeStamp - _SYSVIEW_Globals.LastTxTimeStamp; + MAKE_DELTA_32BIT(Delta); + ENCODE_U32(pPayload, Delta); + // + // Try to store packet in RTT buffer and update time stamp when this was successful + // + Status = SEGGER_RTT_WriteSkipNoLock(CHANNEL_ID_UP, aPacket, pPayload - aPacket); + if (Status) { + _SYSVIEW_Globals.LastTxTimeStamp = TimeStamp; + _SYSVIEW_Globals.EnableState--; // EnableState has been 2, will be 1. Always. + } else { + _SYSVIEW_Globals.DropCount++; + } + // + return Status; +} +#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) + +/********************************************************************* +* +* _SendSyncInfo() +* +* Function description +* Send SystemView sync packet and system information in +* post mortem mode. +* +* Additional information +* Sync is 10 * 0x00 without timestamp +*/ +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) +static void _SendSyncInfo(void) { + // + // Add sync packet ( 10 * 0x00) + // Send system description + // Send system time + // Send task list + // Send module description + // Send module information + // + SEGGER_RTT_WriteWithOverwriteNoLock(CHANNEL_ID_UP, _abSync, 10); + SEGGER_SYSVIEW_RecordVoid(SYSVIEW_EVTID_TRACE_START); + { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, _SYSVIEW_Globals.SysFreq); + ENCODE_U32(pPayload, _SYSVIEW_Globals.CPUFreq); + ENCODE_U32(pPayload, _SYSVIEW_Globals.RAMBaseAddress); + ENCODE_U32(pPayload, SEGGER_SYSVIEW_ID_SHIFT); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_INIT); + RECORD_END(); + } + if (_SYSVIEW_Globals.pfSendSysDesc) { + _SYSVIEW_Globals.pfSendSysDesc(); + } + SEGGER_SYSVIEW_RecordSystime(); + SEGGER_SYSVIEW_SendTaskList(); + if (_NumModules > 0) { + SEGGER_SYSVIEW_SendNumModules(); + for (int n = 0; n < _NumModules; n++) { + SEGGER_SYSVIEW_SendModule(n); + } + SEGGER_SYSVIEW_SendModuleDescription(); + } +} +#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) + +/********************************************************************* +* +* _SendPacket() +* +* Function description +* Send a SystemView packet over RTT. RTT channel and mode are +* configured by macros when the SystemView component is initialized. +* This function takes care of maintaining the packet drop count +* and sending overflow packets when necessary. +* The packet must be passed without Id and Length because this +* function prepends it to the packet before transmission. +* +* Parameters +* pStartPacket - Pointer to start of packet payload. +* There must be at least 4 bytes free to prepend Id and Length. +* pEndPacket - Pointer to end of packet payload. +* EventId - Id of the event to send. +* +*/ +static void _SendPacket(U8* pStartPacket, U8* pEndPacket, unsigned EventId) { + unsigned NumBytes; + U32 TimeStamp; + U32 Delta; +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) + int Status; +#endif + +#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0) + SEGGER_SYSVIEW_LOCK(); +#endif + +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) + if (_SYSVIEW_Globals.EnableState == 0) { + goto SendDone; + } +#else + if (_SYSVIEW_Globals.EnableState == 1) { // Enabled, no dropped packets remaining + goto Send; + } + if (_SYSVIEW_Globals.EnableState == 0) { + goto SendDone; + } + // + // Handle buffer full situations: + // Have packets been dropped before because buffer was full? + // In this case try to send and overflow packet. + // + if (_SYSVIEW_Globals.EnableState == 2) { + _TrySendOverflowPacket(); + if (_SYSVIEW_Globals.EnableState != 1) { + goto SendDone; + } + } +Send: +#endif + // + // Check if event is disabled from being recorded. + // + if (EventId < 32) { + if (_SYSVIEW_Globals.DisabledEvents & ((U32)1u << EventId)) { + goto SendDone; + } + } + // + // Prepare actual packet. + // If it is a known packet, prepend eventId only, + // otherwise prepend packet length and eventId. + // + if (EventId < 24) { + *--pStartPacket = EventId; + } else { + NumBytes = pEndPacket - pStartPacket; + if (NumBytes > 127) { + *--pStartPacket = (NumBytes >> 7); + *--pStartPacket = NumBytes | 0x80; + } else { + *--pStartPacket = NumBytes; + } + if (EventId > 127) { + *--pStartPacket = (EventId >> 7); + *--pStartPacket = EventId | 0x80; + } else { + *--pStartPacket = EventId; + } + } + // + // Compute time stamp delta and append it to packet. + // + TimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); + Delta = TimeStamp - _SYSVIEW_Globals.LastTxTimeStamp; + MAKE_DELTA_32BIT(Delta); + ENCODE_U32(pEndPacket, Delta); +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) + // + // Store packet in RTT buffer by overwriting old data and update time stamp + // + SEGGER_RTT_WriteWithOverwriteNoLock(CHANNEL_ID_UP, pStartPacket, pEndPacket - pStartPacket); + _SYSVIEW_Globals.LastTxTimeStamp = TimeStamp; +#else + // + // Try to store packet in RTT buffer and update time stamp when this was successful + // + Status = SEGGER_RTT_WriteSkipNoLock(CHANNEL_ID_UP, pStartPacket, pEndPacket - pStartPacket); + if (Status) { + _SYSVIEW_Globals.LastTxTimeStamp = TimeStamp; + } else { + _SYSVIEW_Globals.EnableState++; // EnableState has been 1, will be 2. Always. + } +#endif + +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) + // + // Add sync and system information periodically if we are in post mortem mode + // + if (_SYSVIEW_Globals.RecursionCnt == 0) { // Avoid uncontrolled nesting. This way, this routine can call itself once, but no more often than that. + _SYSVIEW_Globals.RecursionCnt = 1; + if (_SYSVIEW_Globals.PacketCount++ & (1 << SEGGER_SYSVIEW_SYNC_PERIOD_SHIFT)) { + _SendSyncInfo(); + _SYSVIEW_Globals.PacketCount = 0; + } + _SYSVIEW_Globals.RecursionCnt = 0; + } +SendDone: + ; // Avoid "label at end of compound statement" error when using static buffer +#else +SendDone: + // + // Check if host is sending data which needs to be processed. + // Note that since this code is called for every packet, it is very time critical, so we do + // only what is really needed here, which is checking if there is any data + // + if (SEGGER_RTT_HASDATA(CHANNEL_ID_DOWN)) { + if (_SYSVIEW_Globals.RecursionCnt == 0) { // Avoid uncontrolled nesting. This way, this routine can call itself once, but no more often than that. + _SYSVIEW_Globals.RecursionCnt = 1; + _HandleIncomingPacket(); + _SYSVIEW_Globals.RecursionCnt = 0; + } + } +#endif + // +#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0) + SEGGER_SYSVIEW_UNLOCK(); // We are done. Unlock and return +#endif +} + +#ifndef SEGGER_SYSVIEW_EXCLUDE_PRINTF // Define in project to avoid warnings about variable parameter list +/********************************************************************* +* +* _APrintHost() +* +* Function description +* Prepares a string and its parameters to be formatted on the host. +* +* Parameters +* s Pointer to format string. +* Options Options to be sent to the host. +* pArguments Pointer to array of arguments for the format string. +* NumArguments Number of arguments in the array. +*/ +static void _APrintHost(const char* s, U32 Options, U32* pArguments, U32 NumArguments) { + U8* pPayload; + U8* pPayloadStart; + + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_ARGUMENTS * SEGGER_SYSVIEW_QUANTA_U32); + pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); + ENCODE_U32(pPayload, Options); + ENCODE_U32(pPayload, NumArguments); + while (NumArguments--) { + ENCODE_U32(pPayload, (*pArguments++)); + } + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); + RECORD_END(); +} + +/********************************************************************* +* +* _VPrintHost() +* +* Function description +* Prepares a string and its parameters to be formatted on the host. +* +* Parameters +* s Pointer to format string. +* Options Options to be sent to the host. +* pParamList Pointer to the list of arguments for the format string. +*/ +static void _VPrintHost(const char* s, U32 Options, va_list* pParamList) { + U32 aParas[SEGGER_SYSVIEW_MAX_ARGUMENTS]; + U32 NumArguments; + const char* p; + + p = s; + NumArguments = 0; + while (*p) { + if (*p == '%') { + aParas[NumArguments++] = va_arg(*pParamList, int); + if (NumArguments == SEGGER_SYSVIEW_MAX_ARGUMENTS) { + break; + } + } + p++; + } + _APrintHost(s, Options, aParas, NumArguments); +} + +/********************************************************************* +* +* _StoreChar() +* +* Function description +* Stores a character in the printf-buffer and sends the buffer when +* it is filled. +* +* Parameters +* p Pointer to the buffer description. +* c Character to be printed. +*/ +static void _StoreChar(SEGGER_SYSVIEW_PRINTF_DESC * p, char c) { + unsigned Cnt; + U8* pPayload; + U32 Options; + + Cnt = p->Cnt; + if ((Cnt + 1u) <= SEGGER_SYSVIEW_MAX_STRING_LEN) { + *(p->pPayload++) = c; + p->Cnt = Cnt + 1u; + } + // + // Write part of string, when the buffer is full + // + if (p->Cnt == SEGGER_SYSVIEW_MAX_STRING_LEN) { + *(p->pPayloadStart) = p->Cnt; + pPayload = p->pPayload; + Options = p->Options; + ENCODE_U32(pPayload, Options); + ENCODE_U32(pPayload, 0); + _SendPacket(p->pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); + p->pPayloadStart = _PreparePacket(p->pBuffer); + p->pPayload = p->pPayloadStart + 1u; + p->Cnt = 0u; + } +} + +/********************************************************************* +* +* _PrintUnsigned() +* +* Function description +* Print an unsigned integer with the given formatting into the +* formatted string. +* +* Parameters +* pBufferDesc Pointer to the buffer description. +* v Value to be printed. +* Base Base of the value. +* NumDigits Number of digits to be printed. +* FieldWidth Width of the printed field. +* FormatFlags Flags for formatting the value. +*/ +static void _PrintUnsigned(SEGGER_SYSVIEW_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) { + static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + unsigned Div; + unsigned Digit; + unsigned Number; + unsigned Width; + char c; + + Number = v; + Digit = 1u; + // + // Get actual field width + // + Width = 1u; + while (Number >= Base) { + Number = (Number / Base); + Width++; + } + if (NumDigits > Width) { + Width = NumDigits; + } + // + // Print leading chars if necessary + // + if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) { + if (FieldWidth != 0u) { + if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) { + c = '0'; + } else { + c = ' '; + } + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, c); + } + } + } + // + // Compute Digit. + // Loop until Digit has the value of the highest digit required. + // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100. + // + while (1) { + if (NumDigits > 1u) { // User specified a min number of digits to print? => Make sure we loop at least that often, before checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned) + NumDigits--; + } else { + Div = v / Digit; + if (Div < Base) { // Is our divider big enough to extract the highest digit from value? => Done + break; + } + } + Digit *= Base; + } + // + // Output digits + // + do { + Div = v / Digit; + v -= Div * Digit; + _StoreChar(pBufferDesc, _aV2C[Div]); + Digit /= Base; + } while (Digit); + // + // Print trailing spaces if necessary + // + if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) { + if (FieldWidth != 0u) { + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, ' '); + } + } + } +} + +/********************************************************************* +* +* _PrintInt() +* +* Function description +* Print a signed integer with the given formatting into the +* formatted string. +* +* Parameters +* pBufferDesc Pointer to the buffer description. +* v Value to be printed. +* Base Base of the value. +* NumDigits Number of digits to be printed. +* FieldWidth Width of the printed field. +* FormatFlags Flags for formatting the value. +*/ +static void _PrintInt(SEGGER_SYSVIEW_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) { + unsigned Width; + int Number; + + Number = (v < 0) ? -v : v; + + // + // Get actual field width + // + Width = 1u; + while (Number >= (int)Base) { + Number = (Number / (int)Base); + Width++; + } + if (NumDigits > Width) { + Width = NumDigits; + } + if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) { + FieldWidth--; + } + + // + // Print leading spaces if necessary + // + if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) { + if (FieldWidth != 0u) { + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, ' '); + } + } + } + // + // Print sign if necessary + // + if (v < 0) { + v = -v; + _StoreChar(pBufferDesc, '-'); + } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) { + _StoreChar(pBufferDesc, '+'); + } else { + + } + // + // Print leading zeros if necessary + // + if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) { + if (FieldWidth != 0u) { + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, '0'); + } + } + } + // + // Print number without sign + // + _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags); +} + +/********************************************************************* +* +* _VPrintTarget() +* +* Function description +* Stores a formatted string. +* This data is read by the host. +* +* Parameters +* sFormat Pointer to format string. +* Options Options to be sent to the host. +* pParamList Pointer to the list of arguments for the format string. +*/ +static void _VPrintTarget(const char* sFormat, U32 Options, va_list* pParamList) { + SEGGER_SYSVIEW_PRINTF_DESC BufferDesc; + char c; + int v; + unsigned NumDigits; + unsigned FormatFlags; + unsigned FieldWidth; + U8* pPayloadStart; +#if SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0 + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 1 + 2 * SEGGER_SYSVIEW_QUANTA_U32); + SEGGER_SYSVIEW_LOCK(); +#else + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 1 + 2 * SEGGER_SYSVIEW_QUANTA_U32); +#endif + +#if SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0 + BufferDesc.pBuffer = aPacket; +#else + BufferDesc.pBuffer = _aPacket; +#endif + BufferDesc.Cnt = 0u; + BufferDesc.pPayloadStart = pPayloadStart; + BufferDesc.pPayload = BufferDesc.pPayloadStart + 1u; + BufferDesc.Options = Options; + + do { + c = *sFormat; + sFormat++; + if (c == 0u) { + break; + } + if (c == '%') { + // + // Filter out flags + // + FormatFlags = 0u; + v = 1; + do { + c = *sFormat; + switch (c) { + case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break; + case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break; + case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break; + case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break; + default: v = 0; break; + } + } while (v); + // + // filter out field with + // + FieldWidth = 0u; + do { + c = *sFormat; + if ((c < '0') || (c > '9')) { + break; + } + sFormat++; + FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0'); + } while (1); + + // + // Filter out precision (number of digits to display) + // + NumDigits = 0u; + c = *sFormat; + if (c == '.') { + sFormat++; + do { + c = *sFormat; + if ((c < '0') || (c > '9')) { + break; + } + sFormat++; + NumDigits = NumDigits * 10u + ((unsigned)c - '0'); + } while (1); + } + // + // Filter out length modifier + // + c = *sFormat; + do { + if ((c == 'l') || (c == 'h')) { + c = *sFormat; + sFormat++; + } else { + break; + } + } while (1); + // + // Handle specifiers + // + switch (c) { + case 'c': { + char c0; + v = va_arg(*pParamList, int); + c0 = (char)v; + _StoreChar(&BufferDesc, c0); + break; + } + case 'd': + v = va_arg(*pParamList, int); + _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags); + break; + case 'u': + v = va_arg(*pParamList, int); + _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags); + break; + case 'x': + case 'X': + v = va_arg(*pParamList, int); + _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags); + break; + case 'p': + v = va_arg(*pParamList, int); + _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u); + break; + case '%': + _StoreChar(&BufferDesc, '%'); + break; + default: + break; + } + sFormat++; + } else { + _StoreChar(&BufferDesc, c); + } + } while (*sFormat); + + // + // Write remaining data, if any + // + if (BufferDesc.Cnt != 0u) { + *(BufferDesc.pPayloadStart) = BufferDesc.Cnt; + ENCODE_U32(BufferDesc.pPayload, BufferDesc.Options); + ENCODE_U32(BufferDesc.pPayload, 0); + _SendPacket(BufferDesc.pPayloadStart, BufferDesc.pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); + } +#if SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0 + SEGGER_SYSVIEW_UNLOCK(); + RECORD_END(); +#else + RECORD_END(); +#endif +} +#endif // SEGGER_SYSVIEW_EXCLUDE_PRINTF + +/********************************************************************* +* +* Public functions +* +********************************************************************** +*/ + +/********************************************************************* +* +* SEGGER_SYSVIEW_Init() +* +* Function description +* Initializes the SYSVIEW module. +* Must be called before SystemViewer attaches to the system. +* +* Parameters +* SysFreq - Frequency of timestamp, i.e. CPU core clock frequency. +* CPUFreq - CPU core clock frequency. +* pOSAPI - Pointer to the API structure for OS-specific functions. +* pfSendSysDesc - Pointer to SendSysDesc callback function. +* +* Additional information +* This function initializes the RTT channel used to transport +* SEGGER SystemView packets. +* The channel is assigned the label "SysView" for client software +* to identify the SystemView channel. +* +* Notes +* The channel is configured by the macro SEGGER_SYSVIEW_RTT_CHANNEL. +*/ +void SEGGER_SYSVIEW_Init(U32 SysFreq, U32 CPUFreq, const SEGGER_SYSVIEW_OS_API *pOSAPI, SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC pfSendSysDesc) { +#ifdef SEGGER_RTT_SECTION + // + // Explicitly initialize the RTT Control Block if it is in its dedicated section. + // + SEGGER_RTT_Init(); +#endif +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) +#if SEGGER_SYSVIEW_RTT_CHANNEL > 0 + SEGGER_RTT_ConfigUpBuffer(SEGGER_SYSVIEW_RTT_CHANNEL, "SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); +#else + _SYSVIEW_Globals.UpChannel = SEGGER_RTT_AllocUpBuffer ("SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); +#endif + _SYSVIEW_Globals.RAMBaseAddress = SEGGER_SYSVIEW_ID_BASE; + _SYSVIEW_Globals.LastTxTimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); + _SYSVIEW_Globals.pOSAPI = pOSAPI; + _SYSVIEW_Globals.SysFreq = SysFreq; + _SYSVIEW_Globals.CPUFreq = CPUFreq; + _SYSVIEW_Globals.pfSendSysDesc = pfSendSysDesc; + _SYSVIEW_Globals.EnableState = 0; + _SYSVIEW_Globals.PacketCount = 0; +#else // (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) +#if SEGGER_SYSVIEW_RTT_CHANNEL > 0 + SEGGER_RTT_ConfigUpBuffer (SEGGER_SYSVIEW_RTT_CHANNEL, "SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); + SEGGER_RTT_ConfigDownBuffer (SEGGER_SYSVIEW_RTT_CHANNEL, "SysView", &_DownBuffer[0], sizeof(_DownBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); +#else + _SYSVIEW_Globals.UpChannel = SEGGER_RTT_AllocUpBuffer ("SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); + // + // TODO: Use SEGGER_RTT_AllocDownBuffer when SystemViewer is able to handle another Down Channel than Up Channel. + // + _SYSVIEW_Globals.DownChannel = _SYSVIEW_Globals.UpChannel; + SEGGER_RTT_ConfigDownBuffer (_SYSVIEW_Globals.DownChannel, "SysView", &_DownBuffer[0], sizeof(_DownBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); +#endif + _SYSVIEW_Globals.RAMBaseAddress = SEGGER_SYSVIEW_ID_BASE; + _SYSVIEW_Globals.LastTxTimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); + _SYSVIEW_Globals.pOSAPI = pOSAPI; + _SYSVIEW_Globals.SysFreq = SysFreq; + _SYSVIEW_Globals.CPUFreq = CPUFreq; + _SYSVIEW_Globals.pfSendSysDesc = pfSendSysDesc; + _SYSVIEW_Globals.EnableState = 0; +#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SetRAMBase() +* +* Function description +* Sets the RAM base address, which is subtracted from IDs in order +* to save bandwidth. +* +* Parameters +* RAMBaseAddress - Lowest RAM Address. (i.e. 0x20000000 on most Cortex-M) +*/ +void SEGGER_SYSVIEW_SetRAMBase(U32 RAMBaseAddress) { + _SYSVIEW_Globals.RAMBaseAddress = RAMBaseAddress; +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordVoid() +* +* Function description +* Formats and sends a SystemView packet with an empty payload. +* +* Parameters +* EventID - SystemView event ID. +*/ +void SEGGER_SYSVIEW_RecordVoid(unsigned EventID) { + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); + // + _SendPacket(pPayloadStart, pPayloadStart, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32() +* +* Function description +* Formats and sends a SystemView packet containing a single U32 +* parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Value - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32(unsigned EventID, U32 Value) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Value); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x2() +* +* Function description +* Formats and sends a SystemView packet containing 2 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x2(unsigned EventID, U32 Para0, U32 Para1) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x3() +* +* Function description +* Formats and sends a SystemView packet containing 3 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x3(unsigned EventID, U32 Para0, U32 Para1, U32 Para2) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 3 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x4() +* +* Function description +* Formats and sends a SystemView packet containing 4 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +* Para3 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x4(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + ENCODE_U32(pPayload, Para3); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x5() +* +* Function description +* Formats and sends a SystemView packet containing 5 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +* Para3 - The 32-bit parameter encoded to SystemView packet payload. +* Para4 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x5(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 5 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + ENCODE_U32(pPayload, Para3); + ENCODE_U32(pPayload, Para4); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x6() +* +* Function description +* Formats and sends a SystemView packet containing 6 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +* Para3 - The 32-bit parameter encoded to SystemView packet payload. +* Para4 - The 32-bit parameter encoded to SystemView packet payload. +* Para5 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x6(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 6 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + ENCODE_U32(pPayload, Para3); + ENCODE_U32(pPayload, Para4); + ENCODE_U32(pPayload, Para5); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x7() +* +* Function description +* Formats and sends a SystemView packet containing 7 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +* Para3 - The 32-bit parameter encoded to SystemView packet payload. +* Para4 - The 32-bit parameter encoded to SystemView packet payload. +* Para5 - The 32-bit parameter encoded to SystemView packet payload. +* Para6 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x7(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 7 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + ENCODE_U32(pPayload, Para3); + ENCODE_U32(pPayload, Para4); + ENCODE_U32(pPayload, Para5); + ENCODE_U32(pPayload, Para6); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x8() +* +* Function description +* Formats and sends a SystemView packet containing 8 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +* Para3 - The 32-bit parameter encoded to SystemView packet payload. +* Para4 - The 32-bit parameter encoded to SystemView packet payload. +* Para5 - The 32-bit parameter encoded to SystemView packet payload. +* Para6 - The 32-bit parameter encoded to SystemView packet payload. +* Para7 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x8(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 8 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + ENCODE_U32(pPayload, Para3); + ENCODE_U32(pPayload, Para4); + ENCODE_U32(pPayload, Para5); + ENCODE_U32(pPayload, Para6); + ENCODE_U32(pPayload, Para7); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x9() +* +* Function description +* Formats and sends a SystemView packet containing 9 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +* Para3 - The 32-bit parameter encoded to SystemView packet payload. +* Para4 - The 32-bit parameter encoded to SystemView packet payload. +* Para5 - The 32-bit parameter encoded to SystemView packet payload. +* Para6 - The 32-bit parameter encoded to SystemView packet payload. +* Para7 - The 32-bit parameter encoded to SystemView packet payload. +* Para8 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x9(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 9 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + ENCODE_U32(pPayload, Para3); + ENCODE_U32(pPayload, Para4); + ENCODE_U32(pPayload, Para5); + ENCODE_U32(pPayload, Para6); + ENCODE_U32(pPayload, Para7); + ENCODE_U32(pPayload, Para8); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordU32x10() +* +* Function description +* Formats and sends a SystemView packet containing 10 U32 parameter payload. +* +* Parameters +* EventID - SystemView event ID. +* Para0 - The 32-bit parameter encoded to SystemView packet payload. +* Para1 - The 32-bit parameter encoded to SystemView packet payload. +* Para2 - The 32-bit parameter encoded to SystemView packet payload. +* Para3 - The 32-bit parameter encoded to SystemView packet payload. +* Para4 - The 32-bit parameter encoded to SystemView packet payload. +* Para5 - The 32-bit parameter encoded to SystemView packet payload. +* Para6 - The 32-bit parameter encoded to SystemView packet payload. +* Para7 - The 32-bit parameter encoded to SystemView packet payload. +* Para8 - The 32-bit parameter encoded to SystemView packet payload. +* Para9 - The 32-bit parameter encoded to SystemView packet payload. +*/ +void SEGGER_SYSVIEW_RecordU32x10(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8, U32 Para9) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 10 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, Para0); + ENCODE_U32(pPayload, Para1); + ENCODE_U32(pPayload, Para2); + ENCODE_U32(pPayload, Para3); + ENCODE_U32(pPayload, Para4); + ENCODE_U32(pPayload, Para5); + ENCODE_U32(pPayload, Para6); + ENCODE_U32(pPayload, Para7); + ENCODE_U32(pPayload, Para8); + ENCODE_U32(pPayload, Para9); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordString() +* +* Function description +* Formats and sends a SystemView packet containing a string. +* +* Parameters +* EventID - SystemView event ID. +* pString - The string to be sent in the SystemView packet payload. +* +* Additional information +* The string is encoded as a count byte followed by the contents +* of the string. +* No more than SEGGER_SYSVIEW_MAX_STRING_LEN bytes will be encoded to the payload. +*/ +void SEGGER_SYSVIEW_RecordString(unsigned EventID, const char* pString) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = _EncodeStr(pPayloadStart, pString, SEGGER_SYSVIEW_MAX_STRING_LEN); + _SendPacket(pPayloadStart, pPayload, EventID); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_Start() +* +* Function description +* Start recording SystemView events. +* This function is triggered by the host application. +* +* Additional information +* This function enables transmission of SystemView packets recorded +* by subsequent trace calls and records a SystemView Start event. +* +* As part of start, a SystemView Init packet is sent, containing the system +* frequency. The list of current tasks, the current system time and the +* system description string is sent, too. +* +* Notes +* SEGGER_SYSVIEW_Start and SEGGER_SYSVIEW_Stop do not nest. +*/ +void SEGGER_SYSVIEW_Start(void) { + if (_SYSVIEW_Globals.EnableState == 0) { + _SYSVIEW_Globals.EnableState = 1; +#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) + _SendSyncInfo(); +#else + SEGGER_SYSVIEW_LOCK(); + SEGGER_RTT_WriteSkipNoLock(CHANNEL_ID_UP, _abSync, 10); + SEGGER_SYSVIEW_UNLOCK(); + SEGGER_SYSVIEW_RecordVoid(SYSVIEW_EVTID_TRACE_START); + { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, _SYSVIEW_Globals.SysFreq); + ENCODE_U32(pPayload, _SYSVIEW_Globals.CPUFreq); + ENCODE_U32(pPayload, _SYSVIEW_Globals.RAMBaseAddress); + ENCODE_U32(pPayload, SEGGER_SYSVIEW_ID_SHIFT); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_INIT); + RECORD_END(); + } + if (_SYSVIEW_Globals.pfSendSysDesc) { + _SYSVIEW_Globals.pfSendSysDesc(); + } + SEGGER_SYSVIEW_RecordSystime(); + SEGGER_SYSVIEW_SendTaskList(); + SEGGER_SYSVIEW_SendNumModules(); +#endif + } +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_Stop() +* +* Function description +* Stop recording SystemView events. +* +* Additional information +* This function disables transmission of SystemView packets recorded +* by subsequent trace calls. If transmission is enabled when +* this function is called, a single SystemView Stop event is recorded +* to the trace, send, and then trace transmission is halted. +*/ +void SEGGER_SYSVIEW_Stop(void) { + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); + // + if (_SYSVIEW_Globals.EnableState) { + _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_TRACE_STOP); + _SYSVIEW_Globals.EnableState = 0; + } + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_GetSysDesc() +* +* Function description +* Triggers a send of the system information and description. +* +*/ +void SEGGER_SYSVIEW_GetSysDesc(void) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, _SYSVIEW_Globals.SysFreq); + ENCODE_U32(pPayload, _SYSVIEW_Globals.CPUFreq); + ENCODE_U32(pPayload, _SYSVIEW_Globals.RAMBaseAddress); + ENCODE_U32(pPayload, SEGGER_SYSVIEW_ID_SHIFT); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_INIT); + RECORD_END(); + if (_SYSVIEW_Globals.pfSendSysDesc) { + _SYSVIEW_Globals.pfSendSysDesc(); + } +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SendTaskInfo() +* +* Function description +* Send a Task Info Packet, containing TaskId for identification, +* task priority and task name. +* +* Parameters +* pInfo - Pointer to task information to send. +*/ +void SEGGER_SYSVIEW_SendTaskInfo(const SEGGER_SYSVIEW_TASKINFO *pInfo) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32 + 1 + 32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, SHRINK_ID(pInfo->TaskID)); + ENCODE_U32(pPayload, pInfo->Prio); + pPayload = _EncodeStr(pPayload, pInfo->sName, 32); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_INFO); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, SHRINK_ID(pInfo->TaskID)); + ENCODE_U32(pPayload, pInfo->StackBase); + ENCODE_U32(pPayload, pInfo->StackSize); + ENCODE_U32(pPayload, 0); // Stack End, future use + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_STACK_INFO); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SendTaskList() +* +* Function description +* Send all tasks descriptors to the host. +*/ +void SEGGER_SYSVIEW_SendTaskList(void) { + if (_SYSVIEW_Globals.pOSAPI && _SYSVIEW_Globals.pOSAPI->pfSendTaskList) { + _SYSVIEW_Globals.pOSAPI->pfSendTaskList(); + } +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SendSysDesc() +* +* Function description +* Send the system description string to the host. +* The system description is used by SystemViewer to identify the +* current application and handle events accordingly. +* +* Parameters +* sSysDesc - Pointer to the 0-terminated system description string. +* +* Additional information +* One system description string may not exceed SEGGER_SYSVIEW_MAX_STRING_LEN characters. +* +* The Following items can be described in a system description string. +* Each item is identified by its identifier, followed by '=' and the value. +* Items are separated by ','. +*/ +void SEGGER_SYSVIEW_SendSysDesc(const char *sSysDesc) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = _EncodeStr(pPayloadStart, sSysDesc, SEGGER_SYSVIEW_MAX_STRING_LEN); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_SYSDESC); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordSystime() +* +* Function description +* Formats and sends a SystemView Systime containing a single U64 or U32 +* parameter payload. +*/ +void SEGGER_SYSVIEW_RecordSystime(void) { + U64 Systime; + + if (_SYSVIEW_Globals.pOSAPI && _SYSVIEW_Globals.pOSAPI->pfGetTime) { + Systime = _SYSVIEW_Globals.pOSAPI->pfGetTime(); + SEGGER_SYSVIEW_RecordU32x2(SYSVIEW_EVTID_SYSTIME_US, + (U32)(Systime), + (U32)(Systime >> 32)); + } else { + SEGGER_SYSVIEW_RecordU32(SYSVIEW_EVTID_SYSTIME_CYCLES, SEGGER_SYSVIEW_GET_TIMESTAMP()); + } +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordEnterISR() +* +* Function description +* Format and send an ISR entry event. +* +* Additional information +* Example packets sent +* 02 0F 50 // ISR(15) Enter. Timestamp is 80 (0x50) +*/ +void SEGGER_SYSVIEW_RecordEnterISR(void) { + unsigned v; + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + v = SEGGER_SYSVIEW_GET_INTERRUPT_ID(); + ENCODE_U32(pPayload, v); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_ISR_ENTER); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordExitISR() +* +* Function description +* Format and send an ISR exit event. +* +* Additional information +* Format as follows: +* 03 // Max. packet len is 6 +* +* Example packets sent +* 03 20 // ISR Exit. Timestamp is 32 (0x20) +*/ +void SEGGER_SYSVIEW_RecordExitISR(void) { + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); + // + _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_ISR_EXIT); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordExitISRToScheduler() +* +* Function description +* Format and send an ISR exit into scheduler event. +* +* Additional information +* Format as follows: +* 18 // Max. packet len is 6 +* +* Example packets sent +* 18 20 // ISR Exit to Scheduler. Timestamp is 32 (0x20) +*/ +void SEGGER_SYSVIEW_RecordExitISRToScheduler(void) { + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); + // + _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_ISR_TO_SCHEDULER); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordEnterTimer() +* +* Function description +* Format and send a Timer entry event. +* +* Parameters +* TimerId - Id of the timer which starts. +*/ +void SEGGER_SYSVIEW_RecordEnterTimer(U32 TimerId) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, SHRINK_ID(TimerId)); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TIMER_ENTER); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordExitTimer() +* +* Function description +* Format and send a Timer exit event. +*/ +void SEGGER_SYSVIEW_RecordExitTimer(void) { + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); + // + _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_TIMER_EXIT); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordEndCall() +* +* Function description +* Format and send an End API Call event without return value. +* +* Parameters +* EventID - Id of API function which ends. +*/ +void SEGGER_SYSVIEW_RecordEndCall(unsigned EventID) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, EventID); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_END_CALL); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordEndCallU32() +* +* Function description +* Format and send an End API Call event with return value. +* +* Parameters +* EventID - Id of API function which ends. +* Para0 - Return value which will be returned by the API function. +*/ +void SEGGER_SYSVIEW_RecordEndCallU32(unsigned EventID, U32 Para0) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, EventID); + ENCODE_U32(pPayload, Para0); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_END_CALL); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnIdle() +* +* Function description +* Record an Idle event. +*/ +void SEGGER_SYSVIEW_OnIdle(void) { + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); + // + _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_IDLE); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnTaskCreate() +* +* Function description +* Record a Task Create event. The Task Create event corresponds +* to creating a task in the OS. +* +* Parameters +* TaskId - Task ID of created task. +*/ +void SEGGER_SYSVIEW_OnTaskCreate(unsigned TaskId) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + TaskId = SHRINK_ID(TaskId); + ENCODE_U32(pPayload, TaskId); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_CREATE); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnTaskTerminate() +* +* Function description +* Record a Task termination event. +* The Task termination event corresponds to terminating a task in +* the OS. If the TaskId is the currently active task, +* SEGGER_SYSVIEW_OnTaskStopExec may be used, either. +* +* Parameters +* TaskId - Task ID of terminated task. +*/ +void SEGGER_SYSVIEW_OnTaskTerminate(unsigned TaskId) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + TaskId = SHRINK_ID(TaskId); + ENCODE_U32(pPayload, TaskId); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_TERMINATE); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnTaskStartExec() +* +* Function description +* Record a Task Start Execution event. The Task Start event +* corresponds to when a task has started to execute rather than +* when it is ready to execute. +* +* Parameters +* TaskId - Task ID of task that started to execute. +*/ +void SEGGER_SYSVIEW_OnTaskStartExec(unsigned TaskId) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + TaskId = SHRINK_ID(TaskId); + ENCODE_U32(pPayload, TaskId); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_START_EXEC); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnTaskStopExec() +* +* Function description +* Record a Task Stop Execution event. The Task Stop event +* corresponds to when a task stops executing and terminates. +*/ +void SEGGER_SYSVIEW_OnTaskStopExec(void) { + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); + // + _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_TASK_STOP_EXEC); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnTaskStartReady() +* +* Function description +* Record a Task Start Ready event. +* +* Parameters +* TaskId - Task ID of task that started to execute. +*/ +void SEGGER_SYSVIEW_OnTaskStartReady(unsigned TaskId) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + TaskId = SHRINK_ID(TaskId); + ENCODE_U32(pPayload, TaskId); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_START_READY); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnTaskStopReady() +* +* Function description +* Record a Task Stop Ready event. +* +* Parameters +* TaskId - Task ID of task that completed execution. +* Cause - Reason for task to stop (i.e. Idle/Sleep) +*/ +void SEGGER_SYSVIEW_OnTaskStopReady(unsigned TaskId, unsigned Cause) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + TaskId = SHRINK_ID(TaskId); + ENCODE_U32(pPayload, TaskId); + ENCODE_U32(pPayload, Cause); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_STOP_READY); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnUserStart() +* +* Function description +* Send a user event start, such as start of a subroutine for profiling. +* +* Parameters +* UserId - User defined ID for the event. +*/ +void SEGGER_SYSVIEW_OnUserStart(unsigned UserId) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, UserId); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_USER_START); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_OnUserStop() +* +* Function description +* Send a user event stop, such as return of a subroutine for profiling. +* +* Parameters +* UserId - User defined ID for the event. +*/ +void SEGGER_SYSVIEW_OnUserStop(unsigned UserId) { + U8 * pPayload; + U8 * pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, UserId); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_USER_STOP); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_NameResource() +* +* Function description +* Send the name of a resource to be displayed in SystemViewer. +* +* Parameters +* ResourceId - Id of the resource to be named. i.e. its address. +* sName - Pointer to the resource name. (Max. SEGGER_SYSVIEW_MAX_STRING_LEN Bytes) +*/ +void SEGGER_SYSVIEW_NameResource(U32 ResourceId, const char* sName) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32 + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = pPayloadStart; + ENCODE_U32(pPayload, SHRINK_ID(ResourceId)); + pPayload = _EncodeStr(pPayload, sName, SEGGER_SYSVIEW_MAX_STRING_LEN); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_NAME_RESOURCE); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SendPacket() +* +* Function description +* Send an event packet. +* +* Parameters +* pPacket - Pointer to the start of the packet. +* pPayloadEnd - Pointer to the end of the payload. +* Make sure there are at least 5 bytes free after the payload. +* EventId - Id of the event packet. +* +* Return value +* !=0: Success, Message sent. +* ==0: Buffer full, Message *NOT* sent. +*/ +int SEGGER_SYSVIEW_SendPacket(U8* pPacket, U8* pPayloadEnd, unsigned EventId) { +#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 1) + SEGGER_SYSVIEW_LOCK(); +#endif + _SendPacket(pPacket + 4, pPayloadEnd, EventId); +#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 1) + SEGGER_SYSVIEW_UNLOCK(); +#endif + return 0; +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_EncodeU32() +* +* Function description +* Encode a U32 in variable-length format. +* +* Parameters +* pPayload - Pointer to where U32 will be encoded. +* Value - The 32-bit value to be encoded. +* +* Return value +* Pointer to the byte following the value, i.e. the first free +* byte in the payload and the next position to store payload +* content. +*/ +U8* SEGGER_SYSVIEW_EncodeU32(U8* pPayload, unsigned Value) { + ENCODE_U32(pPayload, Value); + return pPayload; +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_EncodeString() +* +* Function description +* Encode a string in variable-length format. +* +* Parameters +* pPayload - Pointer to where string will be encoded. +* s - String to encode. +* MaxLen - Maximum number of characters to encode from string. +* +* Return value +* Pointer to the byte following the value, i.e. the first free +* byte in the payload and the next position to store payload +* content. +* +* Additional information +* The string is encoded as a count byte followed by the contents +* of the string. +* No more than 1 + MaxLen bytes will be encoded to the payload. +*/ +U8* SEGGER_SYSVIEW_EncodeString(U8* pPayload, const char* s, unsigned MaxLen) { + return _EncodeStr(pPayload, s, MaxLen); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_EncodeData() +* +* Function description +* Encode a byte buffer in variable-length format. +* +* Parameters +* pPayload - Pointer to where string will be encoded. +* pSrc - Pointer to data buffer to be encoded. +* NumBytes - Number of bytes in the buffer to be encoded. +* +* Return value +* Pointer to the byte following the value, i.e. the first free +* byte in the payload and the next position to store payload +* content. +* +* Additional information +* The data is encoded as a count byte followed by the contents +* of the data buffer. +* Make sure NumBytes + 1 bytes are free for the payload. +*/ +U8* SEGGER_SYSVIEW_EncodeData(U8 *pPayload, const char* pSrc, unsigned NumBytes) { + return _EncodeData(pPayload, pSrc, NumBytes); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_EncodeId() +* +* Function description +* Encode a 32-bit Id in shrunken variable-length format. +* +* Parameters +* pPayload - Pointer to where the Id will be encoded. +* Id - The 32-bit value to be encoded. +* +* Return value +* Pointer to the byte following the value, i.e. the first free +* byte in the payload and the next position to store payload +* content. +* +* Additional information +* The parameters to shrink an Id can be configured in +* SEGGER_SYSVIEW_Conf.h and via SEGGER_SYSVIEW_SetRAMBase(). +* SEGGER_SYSVIEW_ID_BASE: Lowest Id reported by the application. +* (i.e. 0x20000000 when all Ids are an address in this RAM) +* SEGGER_SYSVIEW_ID_SHIFT: Number of bits to shift the Id to +* save bandwidth. (i.e. 2 when Ids are 4 byte aligned) +*/ +U8* SEGGER_SYSVIEW_EncodeId(U8* pPayload, unsigned Id) { + Id = SHRINK_ID(Id); + ENCODE_U32(pPayload, Id); + return pPayload; +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_ShrinkId() +* +* Function description +* Get the shrunken value of an Id for further processing like in +* SEGGER_SYSVIEW_NameResource(). +* +* Parameters +* Id - The 32-bit value to be shrunken. +* +* Return value +* Shrunken Id. +* +* Additional information +* The parameters to shrink an Id can be configured in +* SEGGER_SYSVIEW_Conf.h and via SEGGER_SYSVIEW_SetRAMBase(). +* SEGGER_SYSVIEW_ID_BASE: Lowest Id reported by the application. +* (i.e. 0x20000000 when all Ids are an address in this RAM) +* SEGGER_SYSVIEW_ID_SHIFT: Number of bits to shift the Id to +* save bandwidth. (i.e. 2 when Ids are 4 byte aligned) +*/ +U32 SEGGER_SYSVIEW_ShrinkId(U32 Id) { + return SHRINK_ID(Id); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RegisterModule() +* +* Function description +* Register a middleware module for recording its events. +* +* Parameters +* pModule - The middleware module information. +* +* Additional information +* SEGGER_SYSVIEW_MODULE elements: +* sDescription - Pointer to a string containing the module name and optionally the module event description. +* NumEvents - Number of events the module wants to register. +* EventOffset - Offset to be added to the event Ids. Out parameter, set by this function. Do not modify after calling this function. +* pfSendModuleDesc - Callback function pointer to send more detailed module description to SystemViewer. +* pNext - Pointer to next registered module. Out parameter, set by this function. Do not modify after calling this function. +*/ +void SEGGER_SYSVIEW_RegisterModule(SEGGER_SYSVIEW_MODULE* pModule) { + SEGGER_SYSVIEW_LOCK(); + if (_pFirstModule == 0) { + // + // No module registered, yet. + // Start list with new module. + // EventOffset is the base offset for modules + // + pModule->EventOffset = MODULE_EVENT_OFFSET; + pModule->pNext = 0; + _pFirstModule = pModule; + _NumModules = 1; + } else { + // + // Registreded module(s) present. + // Prepend new module in list. + // EventOffset set from number of events and offset of previous module. + // + pModule->EventOffset = _pFirstModule->EventOffset + _pFirstModule->NumEvents; + pModule->pNext = _pFirstModule; + _pFirstModule = pModule; + _NumModules++; + } + SEGGER_SYSVIEW_SendModule(0); + if (pModule->pfSendModuleDesc) { + pModule->pfSendModuleDesc(); + } + SEGGER_SYSVIEW_UNLOCK(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_RecordModuleDescription() +* +* Function description +* Sends detailed information of a registered module to the host. +* +* Parameters +* pModule - Pointer to the described module. +* sDescription - Pointer to a description string. +*/ +void SEGGER_SYSVIEW_RecordModuleDescription(const SEGGER_SYSVIEW_MODULE* pModule, const char* sDescription) { + U8 ModuleId; + SEGGER_SYSVIEW_MODULE* p; + + p = _pFirstModule; + ModuleId = 0; + do { + if (p == pModule) { + break; + } + ModuleId++; + p = p->pNext; + } while (p); + { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = pPayloadStart; + // + // Send module description + // Send event offset and number of events + // + ENCODE_U32(pPayload, ModuleId); + ENCODE_U32(pPayload, (pModule->EventOffset)); + pPayload = _EncodeStr(pPayload, sDescription, SEGGER_SYSVIEW_MAX_STRING_LEN); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_MODULEDESC); + RECORD_END(); + } +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SendModule() +* +* Function description +* Sends the information of a registered module to the host. +* +* Parameters +* ModuleId - Id of the requested module. +*/ +void SEGGER_SYSVIEW_SendModule(U8 ModuleId) { + SEGGER_SYSVIEW_MODULE* pModule; + U32 n; + + if (_pFirstModule != 0) { + pModule = _pFirstModule; + for (n = 0; n < ModuleId; n++) { + pModule = pModule->pNext; + if (pModule == 0) { + break; + } + } + if (pModule != 0) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = pPayloadStart; + // + // Send module description + // Send event offset and number of events + // + ENCODE_U32(pPayload, ModuleId); + ENCODE_U32(pPayload, (pModule->EventOffset)); + pPayload = _EncodeStr(pPayload, pModule->sModule, SEGGER_SYSVIEW_MAX_STRING_LEN); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_MODULEDESC); + RECORD_END(); + } + } +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SendModuleDescription() +* +* Function description +* Triggers a send of the registered module descriptions. +* +*/ +void SEGGER_SYSVIEW_SendModuleDescription(void) { + SEGGER_SYSVIEW_MODULE* pModule; + + if (_pFirstModule != 0) { + pModule = _pFirstModule; + do { + if (pModule->pfSendModuleDesc) { + pModule->pfSendModuleDesc(); + } + pModule = pModule->pNext; + } while (pModule); + } +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_SendNumModules() +* +* Function description +* Send the number of registered modules to the host. +*/ +void SEGGER_SYSVIEW_SendNumModules(void) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2*SEGGER_SYSVIEW_QUANTA_U32); + pPayload = pPayloadStart; + ENCODE_U32(pPayload, _NumModules); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_NUMMODULES); + RECORD_END(); +} + +#ifndef SEGGER_SYSVIEW_EXCLUDE_PRINTF // Define in project to avoid warnings about variable parameter list + +/********************************************************************* +* +* SEGGER_SYSVIEW_PrintfHostEx() +* +* Function description +* Print a string which is formatted on the host by SystemViewer +* with Additional information. +* +* Parameters +* s - String to be formatted. +* Options - Options for the string. i.e. Log level. +* +* Additional information +* All format arguments are treated as 32-bit scalar values. +*/ +void SEGGER_SYSVIEW_PrintfHostEx(const char* s, U32 Options, ...) { + va_list ParamList; + + va_start(ParamList, Options); + _VPrintHost(s, Options, &ParamList); + va_end(ParamList); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_PrintfHost() +* +* Function description +* Print a string which is formatted on the host by SystemViewer. +* +* Parameters +* s - String to be formatted. +* +* Additional information +* All format arguments are treated as 32-bit scalar values. +*/ +void SEGGER_SYSVIEW_PrintfHost(const char* s, ...) { + va_list ParamList; + + va_start(ParamList, s); + _VPrintHost(s, SEGGER_SYSVIEW_LOG, &ParamList); + va_end(ParamList); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_WarnfHost() +* +* Function description +* Print a warnin string which is formatted on the host by +* SystemViewer. +* +* Parameters +* s - String to be formatted. +* +* Additional information +* All format arguments are treated as 32-bit scalar values. +*/ +void SEGGER_SYSVIEW_WarnfHost(const char* s, ...) { + va_list ParamList; + + va_start(ParamList, s); + _VPrintHost(s, SEGGER_SYSVIEW_WARNING, &ParamList); + va_end(ParamList); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_ErrorfHost() +* +* Function description +* Print an error string which is formatted on the host by +* SystemViewer. +* +* Parameters +* s - String to be formatted. +* +* Additional information +* All format arguments are treated as 32-bit scalar values. +*/ +void SEGGER_SYSVIEW_ErrorfHost(const char* s, ...) { + va_list ParamList; + + va_start(ParamList, s); + _VPrintHost(s, SEGGER_SYSVIEW_ERROR, &ParamList); + va_end(ParamList); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_PrintfTargetEx() +* +* Function description +* Print a string which is formatted on the target before sent to +* the host with Additional information. +* +* Parameters +* s - String to be formatted. +* Options - Options for the string. i.e. Log level. +*/ +void SEGGER_SYSVIEW_PrintfTargetEx(const char* s, U32 Options, ...) { + va_list ParamList; + + va_start(ParamList, Options); + _VPrintTarget(s, Options, &ParamList); + va_end(ParamList); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_PrintfTarget() +* +* Function description +* Print a string which is formatted on the target before sent to +* the host. +* +* Parameters +* s - String to be formatted. +*/ +void SEGGER_SYSVIEW_PrintfTarget(const char* s, ...) { + va_list ParamList; + + va_start(ParamList, s); + _VPrintTarget(s, SEGGER_SYSVIEW_LOG, &ParamList); + va_end(ParamList); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_WarnfTarget() +* +* Function description +* Print a warning string which is formatted on the target before +* sent to the host. +* +* Parameters +* s - String to be formatted. +*/ +void SEGGER_SYSVIEW_WarnfTarget(const char* s, ...) { + va_list ParamList; + + va_start(ParamList, s); + _VPrintTarget(s, SEGGER_SYSVIEW_WARNING, &ParamList); + va_end(ParamList); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_ErrorfTarget() +* +* Function description +* Print an error string which is formatted on the target before +* sent to the host. +* +* Parameters +* s - String to be formatted. +*/ +void SEGGER_SYSVIEW_ErrorfTarget(const char* s, ...) { + va_list ParamList; + + va_start(ParamList, s); + _VPrintTarget(s, SEGGER_SYSVIEW_ERROR, &ParamList); + va_end(ParamList); +} +#endif // SEGGER_SYSVIEW_EXCLUDE_PRINTF + +/********************************************************************* +* +* SEGGER_SYSVIEW_Print() +* +* Function description +* Print a string to the host. +* +* Parameters +* s - String to sent. +*/ +void SEGGER_SYSVIEW_Print(const char* s) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); + ENCODE_U32(pPayload, SEGGER_SYSVIEW_LOG); + ENCODE_U32(pPayload, 0); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_Warn() +* +* Function description +* Print a warning string to the host. +* +* Parameters +* s - String to sent. +*/ +void SEGGER_SYSVIEW_Warn(const char* s) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); + ENCODE_U32(pPayload, SEGGER_SYSVIEW_WARNING); + ENCODE_U32(pPayload, 0); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_Error() +* +* Function description +* Print an error string to the host. +* +* Parameters +* s - String to sent. +*/ +void SEGGER_SYSVIEW_Error(const char* s) { + U8* pPayload; + U8* pPayloadStart; + RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_STRING_LEN); + // + pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); + ENCODE_U32(pPayload, SEGGER_SYSVIEW_ERROR); + ENCODE_U32(pPayload, 0); + _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); + RECORD_END(); +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_EnableEvents() +* +* Function description +* Enable standard SystemView events to be generated. +* +* Parameters +* EnableMask - Events to be enabled. +*/ +void SEGGER_SYSVIEW_EnableEvents(U32 EnableMask) { + _SYSVIEW_Globals.DisabledEvents &= ~EnableMask; +} + +/********************************************************************* +* +* SEGGER_SYSVIEW_DisableEvents() +* +* Function description +* Disable standard SystemView events to not be generated. +* +* Parameters +* DisableMask - Events to be disabled. +*/ +void SEGGER_SYSVIEW_DisableEvents(U32 DisableMask) { + _SYSVIEW_Globals.DisabledEvents |= DisableMask; +} + + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW.h b/src/Segger_SystemView/SEGGER_SYSVIEW.h new file mode 100755 index 00000000..a976152b --- /dev/null +++ b/src/Segger_SystemView/SEGGER_SYSVIEW.h @@ -0,0 +1,334 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +-------------------------- END-OF-HEADER ----------------------------- +File : SEGGER_SYSVIEW.h +Purpose : System visualization API. +Revision: $Rev: 3807 $ +*/ + +#ifndef SEGGER_SYSVIEW_H +#define SEGGER_SYSVIEW_H + +/********************************************************************* +* +* #include Section +* +********************************************************************** +*/ + +#include "SEGGER.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ + +#define SEGGER_SYSVIEW_VERSION 21000 + +#define SEGGER_SYSVIEW_INFO_SIZE 9 // Minimum size, which has to be reserved for a packet. 1-2 byte of message type, 0-2 byte of payload length, 1-5 bytes of timestamp. +#define SEGGER_SYSVIEW_QUANTA_U32 5 // Maximum number of bytes to encode a U32, should be reserved for each 32-bit value in a packet. + +#define SEGGER_SYSVIEW_LOG (0u) +#define SEGGER_SYSVIEW_WARNING (1u) +#define SEGGER_SYSVIEW_ERROR (2u) +#define SEGGER_SYSVIEW_FLAG_APPEND (1u << 6) + +#define SEGGER_SYSVIEW_PREPARE_PACKET(p) (p) + 4 +// +// SystemView events. First 32 IDs from 0 .. 31 are reserved for these +// +#define SYSVIEW_EVTID_NOP 0 // Dummy packet. +#define SYSVIEW_EVTID_OVERFLOW 1 +#define SYSVIEW_EVTID_ISR_ENTER 2 +#define SYSVIEW_EVTID_ISR_EXIT 3 +#define SYSVIEW_EVTID_TASK_START_EXEC 4 +#define SYSVIEW_EVTID_TASK_STOP_EXEC 5 +#define SYSVIEW_EVTID_TASK_START_READY 6 +#define SYSVIEW_EVTID_TASK_STOP_READY 7 +#define SYSVIEW_EVTID_TASK_CREATE 8 +#define SYSVIEW_EVTID_TASK_INFO 9 +#define SYSVIEW_EVTID_TRACE_START 10 +#define SYSVIEW_EVTID_TRACE_STOP 11 +#define SYSVIEW_EVTID_SYSTIME_CYCLES 12 +#define SYSVIEW_EVTID_SYSTIME_US 13 +#define SYSVIEW_EVTID_SYSDESC 14 +#define SYSVIEW_EVTID_USER_START 15 +#define SYSVIEW_EVTID_USER_STOP 16 +#define SYSVIEW_EVTID_IDLE 17 +#define SYSVIEW_EVTID_ISR_TO_SCHEDULER 18 +#define SYSVIEW_EVTID_TIMER_ENTER 19 +#define SYSVIEW_EVTID_TIMER_EXIT 20 +#define SYSVIEW_EVTID_STACK_INFO 21 +#define SYSVIEW_EVTID_MODULEDESC 22 + +#define SYSVIEW_EVTID_INIT 24 +#define SYSVIEW_EVTID_NAME_RESOURCE 25 +#define SYSVIEW_EVTID_PRINT_FORMATTED 26 +#define SYSVIEW_EVTID_NUMMODULES 27 +#define SYSVIEW_EVTID_END_CALL 28 +#define SYSVIEW_EVTID_TASK_TERMINATE 29 + +#define SYSVIEW_EVTID_EX 31 +// +// Event masks to disable/enable events +// +#define SYSVIEW_EVTMASK_NOP (1 << SYSVIEW_EVTID_NOP) +#define SYSVIEW_EVTMASK_OVERFLOW (1 << SYSVIEW_EVTID_OVERFLOW) +#define SYSVIEW_EVTMASK_ISR_ENTER (1 << SYSVIEW_EVTID_ISR_ENTER) +#define SYSVIEW_EVTMASK_ISR_EXIT (1 << SYSVIEW_EVTID_ISR_EXIT) +#define SYSVIEW_EVTMASK_TASK_START_EXEC (1 << SYSVIEW_EVTID_TASK_START_EXEC) +#define SYSVIEW_EVTMASK_TASK_STOP_EXEC (1 << SYSVIEW_EVTID_TASK_STOP_EXEC) +#define SYSVIEW_EVTMASK_TASK_START_READY (1 << SYSVIEW_EVTID_TASK_START_READY) +#define SYSVIEW_EVTMASK_TASK_STOP_READY (1 << SYSVIEW_EVTID_TASK_STOP_READY) +#define SYSVIEW_EVTMASK_TASK_CREATE (1 << SYSVIEW_EVTID_TASK_CREATE) +#define SYSVIEW_EVTMASK_TASK_INFO (1 << SYSVIEW_EVTID_TASK_INFO) +#define SYSVIEW_EVTMASK_TRACE_START (1 << SYSVIEW_EVTID_TRACE_START) +#define SYSVIEW_EVTMASK_TRACE_STOP (1 << SYSVIEW_EVTID_TRACE_STOP) +#define SYSVIEW_EVTMASK_SYSTIME_CYCLES (1 << SYSVIEW_EVTID_SYSTIME_CYCLES) +#define SYSVIEW_EVTMASK_SYSTIME_US (1 << SYSVIEW_EVTID_SYSTIME_US) +#define SYSVIEW_EVTMASK_SYSDESC (1 << SYSVIEW_EVTID_SYSDESC) +#define SYSVIEW_EVTMASK_USER_START (1 << SYSVIEW_EVTID_USER_START) +#define SYSVIEW_EVTMASK_USER_STOP (1 << SYSVIEW_EVTID_USER_STOP) +#define SYSVIEW_EVTMASK_IDLE (1 << SYSVIEW_EVTID_IDLE) +#define SYSVIEW_EVTMASK_ISR_TO_SCHEDULER (1 << SYSVIEW_EVTID_ISR_TO_SCHEDULER) +#define SYSVIEW_EVTMASK_TIMER_ENTER (1 << SYSVIEW_EVTID_TIMER_ENTER) +#define SYSVIEW_EVTMASK_TIMER_EXIT (1 << SYSVIEW_EVTID_TIMER_EXIT) +#define SYSVIEW_EVTMASK_STACK_INFO (1 << SYSVIEW_EVTID_STACK_INFO) +#define SYSVIEW_EVTMASK_MODULEDESC (1 << SYSVIEW_EVTID_MODULEDESC) + +#define SYSVIEW_EVTMASK_INIT (1 << SYSVIEW_EVTID_INIT) +#define SYSVIEW_EVTMASK_NAME_RESOURCE (1 << SYSVIEW_EVTID_NAME_RESOURCE) +#define SYSVIEW_EVTMASK_PRINT_FORMATTED (1 << SYSVIEW_EVTID_PRINT_FORMATTED) +#define SYSVIEW_EVTMASK_NUMMODULES (1 << SYSVIEW_EVTID_NUMMODULES) +#define SYSVIEW_EVTMASK_END_CALL (1 << SYSVIEW_EVTID_END_CALL) +#define SYSVIEW_EVTMASK_TASK_TERMINATE (1 << SYSVIEW_EVTID_TASK_TERMINATE) + +#define SYSVIEW_EVTMASK_EX (1 << SYSVIEW_EVTID_EX) + +#define SYSVIEW_EVTMASK_ALL_INTERRUPTS ( SYSVIEW_EVTMASK_ISR_ENTER \ + | SYSVIEW_EVTMASK_ISR_EXIT \ + | SYSVIEW_EVTMASK_ISR_TO_SCHEDULER) +#define SYSVIEW_EVTMASK_ALL_TASKS ( SYSVIEW_EVTMASK_TASK_START_EXEC \ + | SYSVIEW_EVTMASK_TASK_STOP_EXEC \ + | SYSVIEW_EVTMASK_TASK_START_READY \ + | SYSVIEW_EVTMASK_TASK_STOP_READY \ + | SYSVIEW_EVTMASK_TASK_CREATE \ + | SYSVIEW_EVTMASK_TASK_INFO \ + | SYSVIEW_EVTMASK_STACK_INFO \ + | SYSVIEW_EVTMASK_TASK_TERMINATE) + +/********************************************************************* +* +* Structures +* +********************************************************************** +*/ + +typedef struct { + U32 TaskID; + const char* sName; + U32 Prio; + U32 StackBase; + U32 StackSize; +} SEGGER_SYSVIEW_TASKINFO; + +typedef struct SEGGER_SYSVIEW_MODULE_STRUCT SEGGER_SYSVIEW_MODULE; + +struct SEGGER_SYSVIEW_MODULE_STRUCT { + const char* sModule; + U32 NumEvents; + U32 EventOffset; + void (*pfSendModuleDesc)(void); + SEGGER_SYSVIEW_MODULE* pNext; +}; + +typedef void (SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC)(void); + +/********************************************************************* +* +* API functions +* +********************************************************************** +*/ + +typedef struct { + U64 (*pfGetTime) (void); + void (*pfSendTaskList) (void); +} SEGGER_SYSVIEW_OS_API; + +/********************************************************************* +* +* Control and initialization functions +*/ +void SEGGER_SYSVIEW_Init (U32 SysFreq, U32 CPUFreq, const SEGGER_SYSVIEW_OS_API *pOSAPI, SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC pfSendSysDesc); +void SEGGER_SYSVIEW_SetRAMBase (U32 RAMBaseAddress); +void SEGGER_SYSVIEW_Start (void); +void SEGGER_SYSVIEW_Stop (void); +void SEGGER_SYSVIEW_GetSysDesc (void); +void SEGGER_SYSVIEW_SendTaskList (void); +void SEGGER_SYSVIEW_SendTaskInfo (const SEGGER_SYSVIEW_TASKINFO* pInfo); +void SEGGER_SYSVIEW_SendSysDesc (const char* sSysDesc); + +/********************************************************************* +* +* Event recording functions +*/ +void SEGGER_SYSVIEW_RecordVoid (unsigned EventId); +void SEGGER_SYSVIEW_RecordU32 (unsigned EventId, U32 Para0); +void SEGGER_SYSVIEW_RecordU32x2 (unsigned EventId, U32 Para0, U32 Para1); +void SEGGER_SYSVIEW_RecordU32x3 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2); +void SEGGER_SYSVIEW_RecordU32x4 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3); +void SEGGER_SYSVIEW_RecordU32x5 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4); +void SEGGER_SYSVIEW_RecordU32x6 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5); +void SEGGER_SYSVIEW_RecordU32x7 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6); +void SEGGER_SYSVIEW_RecordU32x8 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7); +void SEGGER_SYSVIEW_RecordU32x9 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8); +void SEGGER_SYSVIEW_RecordU32x10 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8, U32 Para9); +void SEGGER_SYSVIEW_RecordString (unsigned EventId, const char* pString); +void SEGGER_SYSVIEW_RecordSystime (void); +void SEGGER_SYSVIEW_RecordEnterISR (void); +void SEGGER_SYSVIEW_RecordExitISR (void); +void SEGGER_SYSVIEW_RecordExitISRToScheduler (void); +void SEGGER_SYSVIEW_RecordEnterTimer (U32 TimerId); +void SEGGER_SYSVIEW_RecordExitTimer (void); +void SEGGER_SYSVIEW_RecordEndCall (unsigned EventID); +void SEGGER_SYSVIEW_RecordEndCallU32 (unsigned EventID, U32 Para0); + +void SEGGER_SYSVIEW_OnIdle (void); +void SEGGER_SYSVIEW_OnTaskCreate (unsigned TaskId); +void SEGGER_SYSVIEW_OnTaskTerminate (unsigned TaskId); +void SEGGER_SYSVIEW_OnTaskStartExec (unsigned TaskId); +void SEGGER_SYSVIEW_OnTaskStopExec (void); +void SEGGER_SYSVIEW_OnTaskStartReady (unsigned TaskId); +void SEGGER_SYSVIEW_OnTaskStopReady (unsigned TaskId, unsigned Cause); +void SEGGER_SYSVIEW_OnUserStart (unsigned UserId); // Start of user defined event (such as a subroutine to profile) +void SEGGER_SYSVIEW_OnUserStop (unsigned UserId); // Start of user defined event + +void SEGGER_SYSVIEW_NameResource (U32 ResourceId, const char* sName); + +int SEGGER_SYSVIEW_SendPacket (U8* pPacket, U8* pPayloadEnd, unsigned EventId); + +/********************************************************************* +* +* Event parameter encoding functions +*/ +U8* SEGGER_SYSVIEW_EncodeU32 (U8* pPayload, unsigned Value); +U8* SEGGER_SYSVIEW_EncodeData (U8* pPayload, const char* pSrc, unsigned Len); +U8* SEGGER_SYSVIEW_EncodeString (U8* pPayload, const char* s, unsigned MaxLen); +U8* SEGGER_SYSVIEW_EncodeId (U8* pPayload, unsigned Id); +U32 SEGGER_SYSVIEW_ShrinkId (U32 Id); + + +/********************************************************************* +* +* Middleware module registration +*/ +void SEGGER_SYSVIEW_RegisterModule (SEGGER_SYSVIEW_MODULE* pModule); +void SEGGER_SYSVIEW_RecordModuleDescription (const SEGGER_SYSVIEW_MODULE* pModule, const char* sDescription); +void SEGGER_SYSVIEW_SendModule (U8 ModuleId); +void SEGGER_SYSVIEW_SendModuleDescription (void); +void SEGGER_SYSVIEW_SendNumModules (void); + +/********************************************************************* +* +* printf-Style functions +*/ +#ifndef SEGGER_SYSVIEW_EXCLUDE_PRINTF // Define in project to avoid warnings about variable parameter list +void SEGGER_SYSVIEW_PrintfHostEx (const char* s, U32 Options, ...); +void SEGGER_SYSVIEW_PrintfTargetEx (const char* s, U32 Options, ...); +void SEGGER_SYSVIEW_PrintfHost (const char* s, ...); +void SEGGER_SYSVIEW_PrintfTarget (const char* s, ...); +void SEGGER_SYSVIEW_WarnfHost (const char* s, ...); +void SEGGER_SYSVIEW_WarnfTarget (const char* s, ...); +void SEGGER_SYSVIEW_ErrorfHost (const char* s, ...); +void SEGGER_SYSVIEW_ErrorfTarget (const char* s, ...); +#endif + +void SEGGER_SYSVIEW_Print (const char* s); +void SEGGER_SYSVIEW_Warn (const char* s); +void SEGGER_SYSVIEW_Error (const char* s); + +/********************************************************************* +* +* Run-time configuration functions +*/ +void SEGGER_SYSVIEW_EnableEvents (U32 EnableMask); +void SEGGER_SYSVIEW_DisableEvents (U32 DisableMask); + +/********************************************************************* +* +* Application-provided functions +*/ +void SEGGER_SYSVIEW_Conf (void); +U32 SEGGER_SYSVIEW_X_GetTimestamp (void); +U32 SEGGER_SYSVIEW_X_GetInterruptId (void); + +#ifdef __cplusplus +} +#endif + +#endif + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h b/src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h new file mode 100755 index 00000000..a8fa1948 --- /dev/null +++ b/src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h @@ -0,0 +1,160 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +-------------------------- END-OF-HEADER ----------------------------- + +File : SEGGER_SYSVIEW_Conf.h +Purpose : SEGGER SystemView configuration. +Revision: $Rev: 3734 $ +*/ + +#ifndef SEGGER_SYSVIEW_CONF_H +#define SEGGER_SYSVIEW_CONF_H + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ +// +// Constants for known core configuration +// +#define SEGGER_SYSVIEW_CORE_OTHER 0 +#define SEGGER_SYSVIEW_CORE_CM0 1 // Cortex-M0/M0+/M1 +#define SEGGER_SYSVIEW_CORE_CM3 2 // Cortex-M3/M4/M7 +#define SEGGER_SYSVIEW_CORE_RX 3 // Renesas RX + +#if (defined __SES_ARM) || (defined __CROSSWORKS_ARM) || (defined __GNUC__) + #ifdef __ARM_ARCH_6M__ + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM0 + #elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)) + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM3 + #endif +#elif defined(__ICCARM__) + #if (defined (__ARM6M__) && (__CORE__ == __ARM6M__)) + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM0 + #elif ((defined (__ARM7M__) && (__CORE__ == __ARM7M__)) || (defined (__ARM7EM__) && (__CORE__ == __ARM7EM__))) + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM3 + #endif +#elif defined(__CC_ARM) + #if (defined(__TARGET_ARCH_6S_M)) + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM0 + #elif (defined(__TARGET_ARCH_7_M) || defined(__TARGET_ARCH_7E_M)) + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM3 + #endif +#elif defined(__ICCRX__) + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_RX +#elif defined(__RX) + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_RX +#endif + +#ifndef SEGGER_SYSVIEW_CORE + #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_OTHER +#endif + +/********************************************************************* +* +* Defines, configurable +* +********************************************************************** +*/ +/********************************************************************* +* +* SystemView buffer configuration +*/ +#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 // Number of bytes that SystemView uses for the buffer. +#define SEGGER_SYSVIEW_RTT_CHANNEL 1 // The RTT channel that SystemView will use. 0: Auto selection + +#define SEGGER_SYSVIEW_USE_STATIC_BUFFER 1 // Use a static buffer to generate events instead of a buffer on the stack + +#define SEGGER_SYSVIEW_POST_MORTEM_MODE 0 // 1: Enable post mortem analysis mode + +/********************************************************************* +* +* SystemView timestamp configuration +*/ +#if SEGGER_SYSVIEW_CORE == SEGGER_SYSVIEW_CORE_CM3 + #define SEGGER_SYSVIEW_GET_TIMESTAMP() (*(U32 *)(0xE0001004)) // Retrieve a system timestamp. Cortex-M cycle counter. + #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 // Define number of valid bits low-order delivered by clock source +#else + #define SEGGER_SYSVIEW_GET_TIMESTAMP() SEGGER_SYSVIEW_X_GetTimestamp() // Retrieve a system timestamp via user-defined function + #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 // Define number of valid bits low-order delivered by SEGGER_SYSVIEW_X_GetTimestamp() +#endif + +/********************************************************************* +* +* SystemView Id configuration +*/ +#define SEGGER_SYSVIEW_ID_BASE 0x10000000 // Default value for the lowest Id reported by the application. Can be overridden by the application via SEGGER_SYSVIEW_SetRAMBase(). (i.e. 0x20000000 when all Ids are an address in this RAM) +#define SEGGER_SYSVIEW_ID_SHIFT 2 // Number of bits to shift the Id to save bandwidth. (i.e. 2 when Ids are 4 byte aligned) + +/********************************************************************* +* +* SystemView interrupt configuration +*/ +#if SEGGER_SYSVIEW_CORE == SEGGER_SYSVIEW_CORE_CM3 + #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() ((*(U32 *)(0xE000ED04)) & 0x1FF) // Get the currently active interrupt Id. (i.e. read Cortex-M ICSR[8:0] = active vector) +#elif SEGGER_SYSVIEW_CORE == SEGGER_SYSVIEW_CORE_CM0 + #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() ((*(U32 *)(0xE000ED04)) & 0x3F) // Get the currently active interrupt Id. (i.e. read Cortex-M ICSR[5:0] = active vector) +#else + #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() SEGGER_SYSVIEW_X_GetInterruptId() // Get the currently active interrupt Id from the user-provided function. +#endif + +#endif // SEGGER_SYSVIEW_CONF_H + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h b/src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h new file mode 100755 index 00000000..65719407 --- /dev/null +++ b/src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h @@ -0,0 +1,179 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +-------------------------- END-OF-HEADER ----------------------------- +File : SEGGER_SYSVIEW_ConfDefaults.h +Purpose : Defines defaults for configurable defines used in + SEGGER SystemView. +Revision: $Rev: 3734 $ +*/ + +#ifndef SEGGER_SYSVIEW_CONFDEFAULTS_H +#define SEGGER_SYSVIEW_CONFDEFAULTS_H + +/********************************************************************* +* +* #include Section +* +********************************************************************** +*/ + +#include "SEGGER_SYSVIEW_Conf.h" +#include "SEGGER_RTT_Conf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* +* +* Configuration defaults +* +********************************************************************** +*/ + +// Number of bytes that SystemView uses for a buffer. +#ifndef SEGGER_SYSVIEW_RTT_BUFFER_SIZE +//#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 +#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 +#endif + +// The RTT channel that SystemView will use. +#ifndef SEGGER_SYSVIEW_RTT_CHANNEL + #define SEGGER_SYSVIEW_RTT_CHANNEL 0 +#endif +// Sanity check of RTT channel +#if (SEGGER_SYSVIEW_RTT_CHANNEL == 0) && (SEGGER_RTT_MAX_NUM_UP_BUFFERS < 2) + #error "SEGGER_RTT_MAX_NUM_UP_BUFFERS in SEGGER_RTT_Conf.h has to be > 1!" +#elif (SEGGER_SYSVIEW_RTT_CHANNEL >= SEGGER_RTT_MAX_NUM_UP_BUFFERS) + #error "SEGGER_RTT_MAX_NUM_UP_BUFFERS in SEGGER_RTT_Conf.h has to be > SEGGER_SYSVIEW_RTT_CHANNEL!" +#endif + +// Place the SystemView buffer into its own/the RTT section +#if !(defined SEGGER_SYSVIEW_BUFFER_SECTION) && (defined SEGGER_RTT_SECTION) + #define SEGGER_SYSVIEW_BUFFER_SECTION SEGGER_RTT_SECTION +#endif + +// Retrieve a system timestamp. This gets the Cortex-M cycle counter. +#ifndef SEGGER_SYSVIEW_GET_TIMESTAMP + #error "SEGGER_SYSVIEW_GET_TIMESTAMP has to be defined in SEGGER_SYSVIEW_Conf.h!" +#endif + +// Define number of valid bits low-order delivered by clock source. +#ifndef SEGGER_SYSVIEW_TIMESTAMP_BITS + #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 +#endif + +// Lowest Id reported by the Application. +#ifndef SEGGER_SYSVIEW_ID_BASE + #define SEGGER_SYSVIEW_ID_BASE 0 +#endif + +// Number of bits to shift Ids to save bandwidth +#ifndef SEGGER_SYSVIEW_ID_SHIFT + #define SEGGER_SYSVIEW_ID_SHIFT 0 +#endif + +#ifndef SEGGER_SYSVIEW_GET_INTERRUPT_ID + #error "SEGGER_SYSVIEW_GET_INTERRUPT_ID has to be defined in SEGGER_SYSVIEW_Conf.h!" +#endif + +#ifndef SEGGER_SYSVIEW_MAX_ARGUMENTS + #define SEGGER_SYSVIEW_MAX_ARGUMENTS 16 +#endif + +#ifndef SEGGER_SYSVIEW_MAX_STRING_LEN + #define SEGGER_SYSVIEW_MAX_STRING_LEN 128 +#endif + +// Use a static buffer instead of a buffer on the stack for packets +#ifndef SEGGER_SYSVIEW_USE_STATIC_BUFFER + #define SEGGER_SYSVIEW_USE_STATIC_BUFFER 1 +#endif + +// Maximum packet size used by SystemView for the static buffer +#ifndef SEGGER_SYSVIEW_MAX_PACKET_SIZE + #define SEGGER_SYSVIEW_MAX_PACKET_SIZE SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_ARGUMENTS * SEGGER_SYSVIEW_QUANTA_U32 +#endif + +// Use post-mortem analysis instead of real-time analysis +#ifndef SEGGER_SYSVIEW_POST_MORTEM_MODE + #define SEGGER_SYSVIEW_POST_MORTEM_MODE 0 +#endif + +// Configure how frequently syncronization is sent +#ifndef SEGGER_SYSVIEW_SYNC_PERIOD_SHIFT + #define SEGGER_SYSVIEW_SYNC_PERIOD_SHIFT 8 +#endif + +// Lock SystemView (nestable) +#ifndef SEGGER_SYSVIEW_LOCK + #define SEGGER_SYSVIEW_LOCK() SEGGER_RTT_LOCK() +#endif + +// Unlock SystemView (nestable) +#ifndef SEGGER_SYSVIEW_UNLOCK + #define SEGGER_SYSVIEW_UNLOCK() SEGGER_RTT_UNLOCK() +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c b/src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c new file mode 100755 index 00000000..611a1db9 --- /dev/null +++ b/src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c @@ -0,0 +1,115 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +-------------------------- END-OF-HEADER ----------------------------- + +File : SEGGER_SYSVIEW_Config_NoOS.c +Purpose : Sample setup configuration of SystemView without an OS. +Revision: $Rev: 3734 $ +*/ +#include "SEGGER_SYSVIEW.h" + +// SystemcoreClock can be used in most CMSIS compatible projects. +// In non-CMSIS projects define SYSVIEW_CPU_FREQ. +extern unsigned int SystemCoreClock; + +/********************************************************************* +* +* Defines, configurable +* +********************************************************************** +*/ +// The application name to be displayed in SystemViewer +#define SYSVIEW_APP_NAME "Smoothie2" + +// The target device name +#define SYSVIEW_DEVICE_NAME "Cortex-M4" + +// Frequency of the timestamp. Must match SEGGER_SYSVIEW_Conf.h +#define SYSVIEW_TIMESTAMP_FREQ (SystemCoreClock) + +// System Frequency. SystemcoreClock is used in most CMSIS compatible projects. +#define SYSVIEW_CPU_FREQ (SystemCoreClock) + +// The lowest RAM address used for IDs (pointers) +#define SYSVIEW_RAM_BASE (0x10000000) + +/********************************************************************* +* +* _cbSendSystemDesc() +* +* Function description +* Sends SystemView description strings. +*/ +static void _cbSendSystemDesc(void) { + SEGGER_SYSVIEW_SendSysDesc("N="SYSVIEW_APP_NAME",D="SYSVIEW_DEVICE_NAME); + SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick,I#27=acceleration_tick(),I#28=step_tick(),I#29=unstep_tick(),I#30=Timer2,I#100=Endstops::Endstops()"); +} + +/********************************************************************* +* +* Global functions +* +********************************************************************** +*/ +void SEGGER_SYSVIEW_Conf(void) { + SEGGER_SYSVIEW_Init(SYSVIEW_TIMESTAMP_FREQ, SYSVIEW_CPU_FREQ, + 0, _cbSendSystemDesc); + SEGGER_SYSVIEW_SetRAMBase(SYSVIEW_RAM_BASE); +} + +/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_Int.h b/src/Segger_SystemView/SEGGER_SYSVIEW_Int.h new file mode 100755 index 00000000..c04f339d --- /dev/null +++ b/src/Segger_SystemView/SEGGER_SYSVIEW_Int.h @@ -0,0 +1,110 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER SystemView * Real-time application analysis * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* SystemView version: V2.40a * +* * +********************************************************************** +-------------------------- END-OF-HEADER ----------------------------- +File : SEGGER_SYSVIEW_Int.h +Purpose : SEGGER SystemView internal header. +Revision: $Rev: 3734 $ +*/ + +#ifndef SEGGER_SYSVIEW_INT_H +#define SEGGER_SYSVIEW_INT_H + +/********************************************************************* +* +* #include Section +* +********************************************************************** +*/ + +#include "SEGGER_SYSVIEW.h" +#include "SEGGER_SYSVIEW_Conf.h" +#include "SEGGER_SYSVIEW_ConfDefaults.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************************************************************* +* +* Private data types +* +********************************************************************** +*/ +// +// Commands that Host can send to target +// +typedef enum { + SEGGER_SYSVIEW_COMMAND_ID_START = 1, + SEGGER_SYSVIEW_COMMAND_ID_STOP, + SEGGER_SYSVIEW_COMMAND_ID_GET_SYSTIME, + SEGGER_SYSVIEW_COMMAND_ID_GET_TASKLIST, + SEGGER_SYSVIEW_COMMAND_ID_GET_SYSDESC, + SEGGER_SYSVIEW_COMMAND_ID_GET_NUMMODULES, + SEGGER_SYSVIEW_COMMAND_ID_GET_MODULEDESC, + // Extended commands: Commands >= 128 have a second parameter + SEGGER_SYSVIEW_COMMAND_ID_GET_MODULE = 128, +} SEGGER_SYSVIEW_COMMAND_ID; + +#ifdef __cplusplus +} +#endif + +#endif + +/*************************** End of file ****************************/ diff --git a/src/config.default.cartesian b/src/config.default.cartesian new file mode 100644 index 00000000..2d4a4418 --- /dev/null +++ b/src/config.default.cartesian @@ -0,0 +1,195 @@ +# Arm solution configuration : Cartesian robot. Translates mm positions into stepper positions +alpha_steps_per_mm 80 # Steps per mm for alpha stepper +beta_steps_per_mm 80 # Steps per mm for beta stepper +gamma_steps_per_mm 80 # Steps per mm for gamma stepper + +# Planner module configuration : Look-ahead and acceleration configuration +planner_queue_size 32 # DO NOT CHANGE THIS UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING +acceleration 3000 # Acceleration in mm/second/second. +#z_acceleration 500 # Acceleration for Z only moves in mm/s^2, 0 uses acceleration which is the default. DO NOT SET ON A DELTA +acceleration_ticks_per_second 1000 # Number of times per second the speed is updated +junction_deviation 0.05 # Similar to the old "max_jerk", in millimeters, + # see https://github.com/grbl/grbl/blob/master/planner.c + # and https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.8 + # Lower values mean being more careful, higher values means being + # faster and have more jerk +#z_junction_deviation 0.0 # for Z only moves, -1 uses junction_deviation, zero disables junction_deviation on z moves DO NOT SET ON A DELTA +#minimum_planner_speed 0.0 # sets the minimum planner speed in mm/sec + +# Stepper module configuration +microseconds_per_step_pulse 5 # Duration of step pulses to stepper drivers, in microseconds +base_stepping_frequency 100000 # Base frequency for stepping + +# Cartesian axis speed limits +x_axis_max_speed 30000 # mm/min +y_axis_max_speed 30000 # mm/min +z_axis_max_speed 30000 # mm/min + +# Stepper module pins ( ports, and pin numbers, appending "!" to the number will invert a pin ) +alpha_step_pin 3.2 # Pin for alpha stepper step signal +alpha_dir_pin 7.3 # Pin for alpha stepper direction +alpha_en_pin 7.2 # Pin for alpha enable pin +alpha_current 1.5 # X stepper motor current +alpha_max_rate 30000.0 # mm/min + +beta_step_pin 4.2 # Pin for beta stepper step signal +beta_dir_pin 7.0 # Pin for beta stepper direction +beta_en_pin 4.5 # Pin for beta enable +beta_current 1.5 # Y stepper motor current +beta_max_rate 30000.0 # mm/min + +gamma_step_pin 5.7 # Pin for gamma stepper step signal +gamma_dir_pin 5.5 # Pin for gamma stepper direction +gamma_en_pin 7.6 # Pin for gamma enable +gamma_current 1.5 # Z stepper motor current +gamma_max_rate 30000.0 # mm/min + +## System configuration +# Serial communications configuration ( baud rate defaults to 9600 if undefined ) +uart0.baud_rate 115200 # Baud rate for the default hardware serial port +second_usb_serial_enable false # This enables a second usb serial port (to have both pronterface + # and a terminal connected) +#leds_disable true # disable using leds after config loaded +#play_led_disable true # disable the play led + +# Kill button (used to be called pause) maybe assigned to a different pin, set to the onboard pin by default +kill_button_enable false # set to true to enable a kill button +kill_button_pin 2.12 # kill button pin. default is same as pause button 2.12 (2.11 is another good choice) + +#msd_disable false # disable the MSD (USB SDCARD) when set to true (needs special binary) +#dfu_enable false # for linux developers, set to true to enable DFU +#watchdog_timeout 10 # watchdog timeout in seconds, default is 10, set to 0 to disable the watchdog + +# Only needed on a smoothieboard +currentcontrol_module_enable false # + +## Extruder module configuration +extruder.hotend.enable true # Whether to activate the extruder module at all. All configuration is ignored if false +extruder.hotend.steps_per_mm 140 # Steps per mm for extruder stepper +extruder.hotend.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves +extruder.hotend.acceleration 500 # Acceleration for the stepper motor mm/sec² +extruder.hotend.max_speed 50 # mm/s + +extruder.hotend.step_pin 6.4 # Pin for extruder step signal +extruder.hotend.dir_pin 6.5 # Pin for extruder dir signal +extruder.hotend.en_pin 1.7 # Pin for extruder enable signal + + +# Second extruder module configuration +extruder.hotend2.enable false # Whether to activate the extruder module at all. All configuration is ignored if false +extruder.hotend2.steps_per_mm 140 # Steps per mm for extruder stepper +extruder.hotend2.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves +extruder.hotend2.acceleration 500 # Acceleration for the stepper motor, as of 0.6, arbitrary ratio +extruder.hotend2.max_speed 50 # mm/s + +extruder.hotend2.step_pin 6.10 # Pin for extruder step signal +extruder.hotend2.dir_pin 2.9 # Pin for extruder dir signal +extruder.hotend2.en_pin 2.8 # Pin for extruder enable signal + + +## Laser module configuration +laser_module_enable false # Whether to activate the laser module at all. All configuration is + # ignored if false. +## Temperature control configuration +# First hotend configuration +temperature_control.hotend.enable true # Whether to activate this ( "hotend" ) module at all. + # All configuration is ignored if false. +temperature_control.hotend.thermistor_pin adc.5 # Pin for the thermistor to read +temperature_control.hotend.heater_pin 7.5 # Pin that controls the heater, set to nc if a readonly thermistor is being defined +temperature_control.hotend.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5 +#temperature_control.hotend.beta 4066 # or set the beta value +temperature_control.hotend.set_m_code 104 # +temperature_control.hotend.set_and_wait_m_code 109 # +temperature_control.hotend.designator T # +#temperature_control.hotend.max_temp 300 # Set maximum temperature - Will prevent heating above 300 by default +#temperature_control.hotend.min_temp 0 # Set minimum temperature - Will prevent heating below if set + +#temperature_control.hotend.p_factor 13.7 # permanently set the PID values after an auto pid +#temperature_control.hotend.i_factor 0.097 # +#temperature_control.hotend.d_factor 24 # + +#temperature_control.hotend.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v. + +# Second hotend configuration +temperature_control.hotend2.enable false # Whether to activate this ( "hotend" ) module at all. + # All configuration is ignored if false. + +temperature_control.hotend2.thermistor_pin adc.7 # Pin for the thermistor to read +temperature_control.hotend2.heater_pin 7.7 # Pin that controls the heater +temperature_control.hotend2.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5 +#temperature_control.hotend2.beta 4066 # or set the beta value +temperature_control.hotend2.set_m_code 104 # +temperature_control.hotend2.set_and_wait_m_code 109 # +temperature_control.hotend2.designator T1 # + +#temperature_control.hotend2.p_factor 13.7 # permanently set the PID values after an auto pid +#temperature_control.hotend2.i_factor 0.097 # +#temperature_control.hotend2.d_factor 24 # + +#temperature_control.hotend2.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v. + +temperature_control.bed.enable false # +temperature_control.bed.thermistor_pin adc.2 # +temperature_control.bed.heater_pin 7.7 # +temperature_control.bed.thermistor Honeywell100K # see http://smoothieware.org/temperaturecontrol#toc5 +#temperature_control.bed.beta 3974 # or set the beta value + +temperature_control.bed.set_m_code 140 # +temperature_control.bed.set_and_wait_m_code 190 # +temperature_control.bed.designator B # + +#temperature_control.bed.bang_bang false # set to true to use bang bang control rather than PID +#temperature_control.bed.hysteresis 2.0 # set to the temperature in degrees C to use as hysteresis + # when using bang bang + +## Switch module for fan control +switch.fan.enable true # +switch.fan.input_on_command M106 # +switch.fan.input_off_command M107 # +switch.fan.output_pin 4.1 # +switch.fan.output_type pwm # pwm output settable with S parameter in the input_on_comand +switch.fan.max_pwm 255 # set max pwm for the pin default is 255 + +switch.misc.enable true # +switch.misc.input_on_command M42 # +switch.misc.input_off_command M43 # +switch.misc.output_pin 7.7 # +switch.misc.output_type pwm # just an on or off pin + +## Endstops +endstops_enable true # the endstop module is enabled by default and can be disabled here +#corexy_homing false # set to true if homing on a hbot or corexy +alpha_min_endstop 5.3!^ # add a ! to invert if endstop is NO connected to ground +alpha_max_endstop 1.8^ # NOTE set to nc if this is not installed +alpha_homing_direction home_to_min # or set to home_to_max and set alpha_max +alpha_min 0 # this gets loaded after homing when home_to_min is set +alpha_max 200 # this gets loaded after homing when home_to_max is set +beta_min_endstop 9.6!^ # +beta_max_endstop 9.5^ # +beta_homing_direction home_to_min # +beta_min 0 # +beta_max 200 # +gamma_min_endstop 2.13!^ # +gamma_max_endstop 2.12^ # +gamma_homing_direction home_to_min # +gamma_min 0 # +gamma_max 200 # + +# optional enable limit switches, actions will stop if any enabled limit switch is triggered +#alpha_limit_enable false # set to true to enable X min and max limit switches +#beta_limit_enable false # set to true to enable Y min and max limit switches +#gamma_limit_enable false # set to true to enable Z min and max limit switches + +alpha_fast_homing_rate_mm_s 500 # feedrates in mm/second +beta_fast_homing_rate_mm_s 500 # " +gamma_fast_homing_rate_mm_s 40 # " +alpha_slow_homing_rate_mm_s 250 # " +beta_slow_homing_rate_mm_s 250 # " +gamma_slow_homing_rate_mm_s 20 # " + +alpha_homing_retract_mm 5 # distance in mm +beta_homing_retract_mm 5 # " +gamma_homing_retract_mm 1 # " + +#endstop_debounce_count 100 # uncomment if you get noise on your endstops, default is 100 + diff --git a/src/generate-version.bat b/src/generate-version.bat new file mode 100644 index 00000000..0ebf0b8b --- /dev/null +++ b/src/generate-version.bat @@ -0,0 +1,6 @@ +@for /f "delims=" %%a in ('git symbolic-ref HEAD') do @set myvar=%%a +@for /f "delims=" %%b in ('git log "--pretty=format:%%h" -1') do @set myvar2=%%b +@REM the 11 is derived from generate-version.sh "cut -b 12" by subtracting 1 +@echo %myvar:~11%-%myvar2% +@copy /b version.cpp+,, >NUL: + diff --git a/src/generate-version.sh b/src/generate-version.sh new file mode 100755 index 00000000..c58098b9 --- /dev/null +++ b/src/generate-version.sh @@ -0,0 +1,2 @@ +echo `git symbolic-ref HEAD 2> /dev/null | cut -b 12-`-`git log --pretty=format:%h -1` +touch version.cpp \ No newline at end of file diff --git a/src/libs/Debug/HardFault_HandlerC.c b/src/libs/Debug/HardFault_HandlerC.c new file mode 100644 index 00000000..57c85ae3 --- /dev/null +++ b/src/libs/Debug/HardFault_HandlerC.c @@ -0,0 +1,83 @@ +/** + * HardFaultHandler_C: + * This is called from the HardFault_HandlerAsm with a pointer the Fault stack + * as the parameter. We can then read the values from the stack and place them + * into local variables for ease of reading. + * We then read the various Fault Status and Address Registers to help decode + * cause of the fault. + * The function ends with a BKPT instruction to force control back into the debugger + * + * Refer to http://support.code-red-tech.com/CodeRedWiki/DebugHardFault + * + * Also refer to: + * Cortex M4 exception handling + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/ch03s09s01.html + * Cortex M0 exception handing + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/BABBFABJ.html + */ +void HardFault_HandlerC(unsigned long *hardfault_args){ + volatile unsigned long stacked_r0 ; + volatile unsigned long stacked_r1 ; + volatile unsigned long stacked_r2 ; + volatile unsigned long stacked_r3 ; + volatile unsigned long stacked_r12 ; + volatile unsigned long stacked_lr ; + volatile unsigned long stacked_pc ; + volatile unsigned long stacked_psr ; + volatile unsigned long _CFSR ; + volatile unsigned long _HFSR ; + volatile unsigned long _DFSR ; + volatile unsigned long _AFSR ; + volatile unsigned long _BFAR ; + volatile unsigned long _MMAR ; + + stacked_r0 = ((unsigned long)hardfault_args[0]) ; + stacked_r1 = ((unsigned long)hardfault_args[1]) ; + stacked_r2 = ((unsigned long)hardfault_args[2]) ; + stacked_r3 = ((unsigned long)hardfault_args[3]) ; + stacked_r12 = ((unsigned long)hardfault_args[4]) ; + stacked_lr = ((unsigned long)hardfault_args[5]) ; + stacked_pc = ((unsigned long)hardfault_args[6]) ; + stacked_psr = ((unsigned long)hardfault_args[7]) ; + + // Configurable Fault Status Register + // Consists of MMSR, BFSR and UFSR + _CFSR = (*((volatile unsigned long *)(0xE000ED28))) ; + + // Hard Fault Status Register + _HFSR = (*((volatile unsigned long *)(0xE000ED2C))) ; + + // Debug Fault Status Register + _DFSR = (*((volatile unsigned long *)(0xE000ED30))) ; + + // Auxiliary Fault Status Register + _AFSR = (*((volatile unsigned long *)(0xE000ED3C))) ; + + // Read the Fault Address Registers. These may not contain valid values. + // Check BFARVALID/MMARVALID to see if they are valid values + // MemManage Fault Address Register + _MMAR = (*((volatile unsigned long *)(0xE000ED34))) ; + // Bus Fault Address Register + _BFAR = (*((volatile unsigned long *)(0xE000ED38))) ; + + __asm("BKPT #0\n") ; // Break into the debugger +} + +__attribute__((naked)) +PE_ISR(Cpu_ivINT_Hard_Fault) +{ + __asm volatile ( + " movs r0,#4 \n" + " movs r1, lr \n" + " tst r0, r1 \n" + " beq _MSP \n" + " mrs r0, psp \n" + " b _HALT \n" + "_MSP: \n" + " mrs r0, msp \n" + "_HALT: \n" + " ldr r1,[r0,#20] \n" + " b HardFault_HandlerC \n" + " bkpt #0 \n" + ); +} diff --git a/src/modules/tools/extruder/Extruder.cpp b/src/modules/tools/extruder/Extruder.cpp new file mode 100644 index 00000000..df29091a --- /dev/null +++ b/src/modules/tools/extruder/Extruder.cpp @@ -0,0 +1,422 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#include "Extruder.h" + +#include "libs/Module.h" +#include "libs/Kernel.h" + +#include "modules/robot/Conveyor.h" +#include "modules/robot/Block.h" +#include "StepperMotor.h" +#include "SlowTicker.h" +#include "StepTicker.h" +#include "Config.h" +#include "StepperMotor.h" +#include "Robot.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "Gcode.h" +#include "libs/StreamOutput.h" +#include "PublicDataRequest.h" +#include "StreamOutputPool.h" +#include "ExtruderPublicAccess.h" +#include "nuts_bolts.h" + +#include + +// OLD config names for backwards compatibility, NOTE new configs will not be added here +#define extruder_module_enable_checksum CHECKSUM("extruder_module_enable") +#define extruder_steps_per_mm_checksum CHECKSUM("extruder_steps_per_mm") +#define extruder_filament_diameter_checksum CHECKSUM("extruder_filament_diameter") +#define extruder_acceleration_checksum CHECKSUM("extruder_acceleration") +#define extruder_step_pin_checksum CHECKSUM("extruder_step_pin") +#define extruder_dir_pin_checksum CHECKSUM("extruder_dir_pin") +#define extruder_en_pin_checksum CHECKSUM("extruder_en_pin") +#define extruder_max_speed_checksum CHECKSUM("extruder_max_speed") +#define extruder_default_feed_rate_checksum CHECKSUM("extruder_default_feed_rate") + +// NEW config names + +#define default_feed_rate_checksum CHECKSUM("default_feed_rate") +#define steps_per_mm_checksum CHECKSUM("steps_per_mm") +#define filament_diameter_checksum CHECKSUM("filament_diameter") +#define acceleration_checksum CHECKSUM("acceleration") +#define step_pin_checksum CHECKSUM("step_pin") +#define dir_pin_checksum CHECKSUM("dir_pin") +#define en_pin_checksum CHECKSUM("en_pin") +#define max_speed_checksum CHECKSUM("max_speed") +#define x_offset_checksum CHECKSUM("x_offset") +#define y_offset_checksum CHECKSUM("y_offset") +#define z_offset_checksum CHECKSUM("z_offset") + +#define retract_length_checksum CHECKSUM("retract_length") +#define retract_feedrate_checksum CHECKSUM("retract_feedrate") +#define retract_recover_length_checksum CHECKSUM("retract_recover_length") +#define retract_recover_feedrate_checksum CHECKSUM("retract_recover_feedrate") +#define retract_zlift_length_checksum CHECKSUM("retract_zlift_length") +#define retract_zlift_feedrate_checksum CHECKSUM("retract_zlift_feedrate") + +#define PI 3.14159265358979F + + +/* + As the actual motion is handled by the planner and the stepticker, this module just handles Extruder specific gcodes + and settings. + In a multi extruder setting it must be selected to be addressed. (using T0 T1 etc) +*/ + +Extruder::Extruder( uint16_t config_identifier) +{ + this->selected = false; + this->identifier = config_identifier; + this->retracted = false; + this->volumetric_multiplier = 1.0F; + this->extruder_multiplier = 1.0F; + this->stepper_motor = nullptr; + this->max_volumetric_rate = 0; + this->g92e0_detected = false; + memset(this->offset, 0, sizeof(this->offset)); +} + +Extruder::~Extruder() +{ + delete stepper_motor; +} + +void Extruder::on_module_loaded() +{ + // Settings + this->config_load(); + + // We work on the same Block as Stepper, so we need to know when it gets a new one and drops one + this->register_for_event(ON_GCODE_RECEIVED); + this->register_for_event(ON_GET_PUBLIC_DATA); + this->register_for_event(ON_SET_PUBLIC_DATA); +} + +// Get config +void Extruder::config_load() +{ + + Pin step_pin, dir_pin, en_pin; + step_pin.from_string( THEKERNEL->config->value(extruder_checksum, this->identifier, step_pin_checksum )->by_default("nc" )->as_string())->as_output(); + dir_pin.from_string( THEKERNEL->config->value(extruder_checksum, this->identifier, dir_pin_checksum )->by_default("nc" )->as_string())->as_output(); + en_pin.from_string( THEKERNEL->config->value(extruder_checksum, this->identifier, en_pin_checksum )->by_default("nc" )->as_string())->as_output(); + + float steps_per_millimeter = THEKERNEL->config->value(extruder_checksum, this->identifier, steps_per_mm_checksum)->by_default(1)->as_number(); +//TODO Is this still needed float acceleration = THEKERNEL->config->value(extruder_checksum, this->identifier, acceleration_checksum)->by_default(1000)->as_number(); + + this->offset[X_AXIS] = THEKERNEL->config->value(extruder_checksum, this->identifier, x_offset_checksum )->by_default(0)->as_number(); + this->offset[Y_AXIS] = THEKERNEL->config->value(extruder_checksum, this->identifier, y_offset_checksum )->by_default(0)->as_number(); + this->offset[Z_AXIS] = THEKERNEL->config->value(extruder_checksum, this->identifier, z_offset_checksum )->by_default(0)->as_number(); + + this->filament_diameter = THEKERNEL->config->value(extruder_checksum, this->identifier, filament_diameter_checksum )->by_default(0)->as_number(); + this->retract_length = THEKERNEL->config->value(extruder_checksum, this->identifier, retract_length_checksum)->by_default(3)->as_number(); + this->retract_feedrate = THEKERNEL->config->value(extruder_checksum, this->identifier, retract_feedrate_checksum)->by_default(45)->as_number(); + this->retract_recover_length = THEKERNEL->config->value(extruder_checksum, this->identifier, retract_recover_length_checksum)->by_default(0)->as_number(); + this->retract_recover_feedrate = THEKERNEL->config->value(extruder_checksum, this->identifier, retract_recover_feedrate_checksum)->by_default(8)->as_number(); + this->retract_zlift_length = THEKERNEL->config->value(extruder_checksum, this->identifier, retract_zlift_length_checksum)->by_default(0)->as_number(); + this->retract_zlift_feedrate = THEKERNEL->config->value(extruder_checksum, this->identifier, retract_zlift_feedrate_checksum)->by_default(100 * 60)->as_number() / 60.0F; // mm/min + + if(filament_diameter > 0.01F) { + this->volumetric_multiplier = 1.0F / (powf(this->filament_diameter / 2, 2) * PI); + } + + // Stepper motor object for the extruder + stepper_motor = new StepperMotor(step_pin, dir_pin, en_pin); + //motor_id = THEROBOT->register_motor(stepper_motor); + motor_id = THEKERNEL->step_ticker->register_motor(stepper_motor); + + stepper_motor->set_max_rate(THEKERNEL->config->value(extruder_checksum, this->identifier, max_speed_checksum)->by_default(1000)->as_number()); +//TODO is this still required stepper_motor->set_acceleration(acceleration); + stepper_motor->change_steps_per_mm(steps_per_millimeter); +//TODO is this still required stepper_motor->set_selected(false); // not selected by default +} + +void Extruder::select() +{ + selected = true; +//TODO is this still required stepper_motor->set_selected(true); + // set the function pointer to return the current scaling +//TODO is this still required THEROBOT->get_e_scale_fnc = std::bind(&Extruder::get_e_scale, this); +} + +void Extruder::deselect() +{ + selected = false; +//TODO is this still required stepper_motor->set_selected(false); +//TODO is this still required THEROBOT->get_e_scale_fnc = nullptr; +} + +void Extruder::on_get_public_data(void *argument) +{ + PublicDataRequest *pdr = static_cast(argument); + + if(!pdr->starts_with(extruder_checksum)) return; + + if(!this->selected) return; + + // pointer to structure to return data to is provided + pad_extruder_t *e = static_cast(pdr->get_data_ptr()); + e->steps_per_mm = stepper_motor->get_steps_per_mm(); + e->filament_diameter = this->filament_diameter; + e->flow_rate = this->extruder_multiplier; +//TODO is this still required e->accleration = stepper_motor->get_acceleration(); + e->retract_length = this->retract_length; + e->current_position = stepper_motor->get_current_position(); + pdr->set_taken(); +} + +void Extruder::on_set_public_data(void *argument) +{ + PublicDataRequest *pdr = static_cast(argument); + + if(!pdr->starts_with(extruder_checksum)) return; + + // handle extrude rates request from robot + if(pdr->second_element_is(target_checksum)) { + // disabled extruders do not reply NOTE only one enabled extruder supported + if(!this->selected) return; + + float *d = static_cast(pdr->get_data_ptr()); + float delta = d[0]; // the E passed in on Gcode is the delta volume in mm³ + float isecs = d[1]; // inverted secs + + // check against maximum speeds and return rate modifier + d[1] = check_max_speeds(delta, isecs); + pdr->set_taken(); + return; + } + + // save or restore extruder state + if(pdr->second_element_is(save_state_checksum)) { + save_position(); + this->saved_selected = this->selected; + pdr->set_taken(); + + } else if(pdr->second_element_is(restore_state_checksum)) { + this->selected = this->saved_selected; + // NOTE this only gets called when the queue is empty so the milestones will be the same + restore_position(); + pdr->set_taken(); + } +} + +void Extruder::save_position() +{ + // we need to save these separately as they may have been scaled +//TODO is this still required this->saved_position = std::make_tuple(THEKERNEL->robot->get_axis_position(motor_id), stepper_motor->get_last_milestone(), stepper_motor->get_last_milestone_steps()); +} + +void Extruder::restore_position() +{ + //TODO is this still required THEKERNEL->robot->->reset_axis_position(std::get<0>(this->saved_position), motor_id); +//TODO is this still required stepper_motor->set_last_milestones(std::get<1>(this->saved_position), std::get<2>(this->saved_position)); +} + +// check against maximum speeds and return the rate modifier +float Extruder::check_max_speeds(float delta, float isecs) +{ + float rm = 1.0F; // default no rate modification + + if(this->max_volumetric_rate > 0 && this->filament_diameter > 0.01F) { + // volumetric enabled and check for volumetric rate + float v = delta * isecs; // the flow rate in mm³/sec + + // return the rate change needed to stay within the max rate + if(v > max_volumetric_rate) { + rm = max_volumetric_rate / v; + } + //THEKERNEL->streams->printf("requested flow rate: %f mm³/sec, corrected flow rate: %f mm³/sec\n", v, v * rm); + } + + return rm; +} + +void Extruder::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + + // M codes most execute immediately, most only execute if enabled + if (gcode->has_m) { + if (gcode->m == 114 && this->selected) { + char buf[16]; + if(gcode->subcode == 0) { +//TODO ADDBACK float pos = THEKERNEL->robot->get_axis_position(motor_id); +//TODO ADDBACK int n = snprintf(buf, sizeof(buf), " E:%1.3f ", pos); +//TODO ADDBACK gcode->txt_after_ok.append(buf, n); + + } else if(gcode->subcode == 1) { // realtime position + int n = snprintf(buf, sizeof(buf), " E:%1.3f ", stepper_motor->get_current_position() / get_e_scale()); + gcode->txt_after_ok.append(buf, n); + + } else if(gcode->subcode == 3) { // realtime actuator position + int n = snprintf(buf, sizeof(buf), " E:%1.3f ", stepper_motor->get_current_position()); + gcode->txt_after_ok.append(buf, n); + } + + + } else if (gcode->m == 92 && ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier) ) ) { + float spm = stepper_motor->get_steps_per_mm(); + if (gcode->has_letter('E')) { + spm = gcode->get_value('E'); + stepper_motor->change_steps_per_mm(spm); + } + + gcode->stream->printf("E:%f ", spm); + gcode->add_nl = true; + + } else if (gcode->m == 200 && ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier)) ) { + if (gcode->has_letter('D')) { + this->filament_diameter = gcode->get_value('D'); + float last_scale = this->volumetric_multiplier; + if(filament_diameter > 0.01F) { + this->volumetric_multiplier = 1.0F / (powf(this->filament_diameter / 2, 2) * PI); + } else { + this->volumetric_multiplier = 1.0F; + } + // the trouble here is that the last milestone will be for the previous multiplier so a change can cause a big blob + // so we must change the E last milestone accordingly so it continues smoothly.... + // change E last milestone to what it would have been if it had used this new multiplier + float delta = this->volumetric_multiplier / last_scale; + float nm = this->stepper_motor->get_last_milestone() * delta; + this->stepper_motor->change_last_milestone(nm); + + } else { + if(filament_diameter > 0.01F) { + gcode->stream->printf("Filament Diameter: %f\n", this->filament_diameter); + } else { + gcode->stream->printf("Volumetric extrusion is disabled\n"); + } + } + + } else if (gcode->m == 203 && ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier)) ) { + // M203 Exxx Vyyy Set maximum feedrates xxx mm/sec and/or yyy mm³/sec + if(gcode->get_num_args() == 0) { + gcode->stream->printf("E:%g V:%g", this->stepper_motor->get_max_rate(), this->max_volumetric_rate); + gcode->add_nl = true; + + } else { + if(gcode->has_letter('E')) { + this->stepper_motor->set_max_rate(gcode->get_value('E')); + } + if(gcode->has_letter('V')) { + this->max_volumetric_rate = gcode->get_value('V'); + } + } + + } else if (gcode->m == 204 && gcode->has_letter('E') && + ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier)) ) { + // extruder acceleration M204 Ennn mm/sec^2 (Pnnn sets the specific extruder for M500) +//TODO is this still required stepper_motor->set_acceleration(gcode->get_value('E')); + + } else if (gcode->m == 207 && ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier)) ) { + // M207 - set retract length S[positive mm] F[feedrate mm/min] Z[additional zlift/hop] Q[zlift feedrate mm/min] + if(gcode->has_letter('S')) retract_length = gcode->get_value('S'); + if(gcode->has_letter('F')) retract_feedrate = gcode->get_value('F') / 60.0F; // specified in mm/min converted to mm/sec + if(gcode->has_letter('Z')) retract_zlift_length = gcode->get_value('Z'); // specified in mm + if(gcode->has_letter('Q')) retract_zlift_feedrate = gcode->get_value('Q') / 60.0F; // specified in mm/min converted to mm/sec + + } else if (gcode->m == 208 && ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier)) ) { + // M208 - set retract recover length S[positive mm surplus to the M207 S*] F[feedrate mm/min] + if(gcode->has_letter('S')) retract_recover_length = gcode->get_value('S'); + if(gcode->has_letter('F')) retract_recover_feedrate = gcode->get_value('F') / 60.0F; // specified in mm/min converted to mm/sec + + } else if (gcode->m == 221 && this->selected) { // M221 S100 change flow rate by percentage + if(gcode->has_letter('S')) { + float last_scale = this->extruder_multiplier; + this->extruder_multiplier = gcode->get_value('S') / 100.0F; + // the trouble here is that the last milestone will be for the previous multiplier so a change can cause a big blob + // so we must change the E last milestone accordingly so it continues smoothly.... + // change E last milestone to what it would have been if it had used this new multiplier + float delta = this->extruder_multiplier / last_scale; + float nm = this->stepper_motor->get_last_milestone() * delta; + this->stepper_motor->change_last_milestone(nm); + + } else { + gcode->stream->printf("Flow rate at %6.2f %%\n", this->extruder_multiplier * 100.0F); + } + + } else if (gcode->m == 500 || gcode->m == 503) { // M500 saves some volatile settings to config override file, M503 just prints the settings + gcode->stream->printf(";E Steps per mm:\nM92 E%1.4f P%d\n", stepper_motor->get_steps_per_mm(), this->identifier); + gcode->stream->printf(";E Filament diameter:\nM200 D%1.4f P%d\n", this->filament_diameter, this->identifier); + gcode->stream->printf(";E retract length, feedrate:\nM207 S%1.4f F%1.4f Z%1.4f Q%1.4f P%d\n", this->retract_length, this->retract_feedrate * 60.0F, this->retract_zlift_length, this->retract_zlift_feedrate * 60.0F, this->identifier); + gcode->stream->printf(";E retract recover length, feedrate:\nM208 S%1.4f F%1.4f P%d\n", this->retract_recover_length, this->retract_recover_feedrate * 60.0F, this->identifier); +//TODO is this still required gcode->stream->printf(";E acceleration mm/sec²:\nM204 E%1.4f P%d\n", stepper_motor->get_acceleration(), this->identifier); + gcode->stream->printf(";E max feed rate mm/sec:\nM203 E%1.4f P%d\n", stepper_motor->get_max_rate(), this->identifier); + if(this->max_volumetric_rate > 0) { + gcode->stream->printf(";E max volumetric rate mm³/sec:\nM203 V%1.4f P%d\n", this->max_volumetric_rate, this->identifier); + } + } + + } else if( gcode->has_g && this->selected ) { + + if( (gcode->g == 10 || gcode->g == 11) && !gcode->has_letter('L') ) { + // firmware retract command (Ignore if has L parameter that is not for us) + // check we are in the correct state of retract or unretract + if(gcode->g == 10 && !retracted) { + this->retracted = true; + this->cancel_zlift_restore = false; + this->g92e0_detected = false; + } else if(gcode->g == 11 && retracted) { + this->retracted = false; + } else + return; // ignore duplicates + + if(gcode->g == 10) { + // retract + float delta[motor_id + 1]; + for (int i = 0; i < motor_id; ++i) { + delta[i] = 0; + } + + delta[motor_id] = -retract_length / get_e_scale(); // convert from mm to mm³, and unapply flow_rate + THEKERNEL->robot->delta_move(delta, retract_feedrate, motor_id + 1); + + // zlift + if(retract_zlift_length > 0) { + float delta[3] {0, 0, retract_zlift_length}; + THEKERNEL->robot->delta_move(delta, retract_zlift_feedrate, 3); + } + + } else if(gcode->g == 11) { + // unretract + if(retract_zlift_length > 0 && !this->cancel_zlift_restore) { + // reverse zlift happens before unretract + // NOTE we do not do this if cancel_zlift_restore is set to true, which happens if there is an absolute Z move inbetween G10 and G11 + float delta[3] {0, 0, -retract_zlift_length}; + THEKERNEL->robot->delta_move(delta, retract_zlift_feedrate, 3); + } + + float delta[motor_id + 1]; + for (int i = 0; i < motor_id; ++i) { + delta[i] = 0; + } + // HACK ALERT due to certain slicers reseting E with G92 E0 between the G10 and G11 we need to restore + // the current position after we do the unretract, this is horribly hacky :( + // also as the move has not completed yet, when we restore the current position will be incorrect once the move finishes, + // however this is not fatal for an extruder + if(g92e0_detected) save_position(); + delta[motor_id] = (retract_length + retract_recover_length) / get_e_scale(); // convert from mm to mm³, and unapply flow_rate + THEKERNEL->robot->delta_move(delta, retract_recover_feedrate, motor_id + 1); + if(g92e0_detected) restore_position(); + } + + + } else if( this->retracted && (gcode->g == 0 || gcode->g == 1) && gcode->has_letter('Z')) { + // NOTE we cancel the zlift restore for the following G11 as we have moved to an absolute Z which we need to stay at + this->cancel_zlift_restore = true; + + } else if( this->retracted && gcode->g == 92 && gcode->has_letter('E')) { + // old versions of slic3rs issued a G92 E0 after G10, handle that case + this->g92e0_detected= true; + } + + } + +} diff --git a/src/modules/tools/extruder/Extruder.h b/src/modules/tools/extruder/Extruder.h new file mode 100644 index 00000000..430162a9 --- /dev/null +++ b/src/modules/tools/extruder/Extruder.h @@ -0,0 +1,66 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + + + +#pragma once + +#include "Tool.h" +#include "Pin.h" + +#include + +class StepperMotor; + +// NOTE Tool is also a module, no need for multiple inheritance here +class Extruder : public Tool { + public: + Extruder(uint16_t config_identifier); + virtual ~Extruder(); + + void on_module_loaded(); + void on_gcode_received(void*); + + void select(); + void deselect(); + float get_e_scale(void) const { return volumetric_multiplier * extruder_multiplier; } + + private: + void config_load(); + void on_get_public_data(void* argument); + void on_set_public_data(void* argument); + float check_max_speeds(float target, float isecs); + void save_position(); + void restore_position(); + + StepperMotor *stepper_motor; + + float extruder_multiplier; // flow rate 1.0 == 100% + float filament_diameter; // filament diameter + float volumetric_multiplier; + float max_volumetric_rate; // used for calculating volumetric rate in mm³/sec + + // for firmware retract + float retract_length; // firmware retract length + float retract_feedrate; + float retract_recover_feedrate; + float retract_recover_length; + float retract_zlift_length; + float retract_zlift_feedrate; + + // for saving and restoring extruder position + std::tuple saved_position; + + struct { + uint8_t motor_id:8; + bool retracted:1; + bool cancel_zlift_restore:1; // hack to stop a G11 zlift restore from overring an absolute Z setting + bool selected:1; + bool saved_selected:1; + bool g92e0_detected:1; + }; +}; diff --git a/src/modules/tools/extruder/ExtruderMaker.cpp b/src/modules/tools/extruder/ExtruderMaker.cpp new file mode 100644 index 00000000..fd89ddcb --- /dev/null +++ b/src/modules/tools/extruder/ExtruderMaker.cpp @@ -0,0 +1,90 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#include "ExtruderMaker.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "Extruder.h" +#include "Config.h" +#include "ToolManager.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "StreamOutputPool.h" + +#include +#include +using namespace std; + +#define extruder_checksum CHECKSUM("extruder") +#define enable_checksum CHECKSUM("enable") + +void ExtruderMaker::load_tools(){ + + // Get every "declared" extruder module + vector modules; + THEKERNEL->config->get_module_list( &modules, extruder_checksum ); + + if(modules.size() == 0) { + THEKERNEL->streams->printf("NOTE: No extruders configured\n"); + return; + } + + // count number of enabled extruders + int cnt= 0; + for(auto cs : modules) { + if( THEKERNEL->config->value(extruder_checksum, cs, enable_checksum )->as_bool() ){ + cnt++; + } + } + + if(cnt == 0) { + THEKERNEL->streams->printf("NOTE: No extruders enabled\n"); + return; + } + + ToolManager *toolmanager= nullptr; + if(cnt > 1) { + // ONLY do this if multitool enabled and more than one tool is defined + toolmanager= new ToolManager(); + THEKERNEL->add_module( toolmanager ); + + }else{ + // only one extruder so no tool manager required + THEKERNEL->streams->printf("NOTE: One extruder configured and enabled\n"); + } + + + // For every extruder found, setup the enabled ones + for(auto cs : modules){ + // If module is enabled + if( THEKERNEL->config->value(extruder_checksum, cs, enable_checksum )->as_bool() ){ + + // Make a new extruder module + Extruder* extruder = new Extruder(cs); + + // Add the Extruder module to the kernel + THEKERNEL->add_module( extruder ); + + if(toolmanager != nullptr) { + // Add the extruder module to the ToolsManager if it was created + toolmanager->add_tool( extruder ); + + }else{ + // if not managed by toolmanager we need to enable the one extruder + extruder->select(); + } + } + + } + + THEKERNEL->streams->printf("NOTE: %d extruders enabled out of %d\n", cnt, modules.size()); +} + + + + + diff --git a/src/modules/tools/extruder/ExtruderMaker.h b/src/modules/tools/extruder/ExtruderMaker.h new file mode 100644 index 00000000..a4939aca --- /dev/null +++ b/src/modules/tools/extruder/ExtruderMaker.h @@ -0,0 +1,18 @@ +/* + this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl). + smoothie 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. + smoothie 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 smoothie. if not, see . +*/ + +#ifndef EXTRUDERMAKER_H +#define EXTRUDERMAKER_H + +class ExtruderMaker { + public: + void load_tools(); +}; + + + +#endif diff --git a/src/modules/tools/extruder/ExtruderPublicAccess.h b/src/modules/tools/extruder/ExtruderPublicAccess.h new file mode 100644 index 00000000..6b1796ee --- /dev/null +++ b/src/modules/tools/extruder/ExtruderPublicAccess.h @@ -0,0 +1,15 @@ +#pragma once + +#define extruder_checksum CHECKSUM("extruder") +#define save_state_checksum CHECKSUM("save_state") +#define restore_state_checksum CHECKSUM("restore_state") +#define target_checksum CHECKSUM("target") + +using pad_extruder_t = struct pad_extruder { + float steps_per_mm; + float filament_diameter; + float flow_rate; + float accleration; + float retract_length; + float current_position; +}; diff --git a/src/modules/tools/filamentdetector/FilamentDetector.cpp b/src/modules/tools/filamentdetector/FilamentDetector.cpp new file mode 100644 index 00000000..327443e4 --- /dev/null +++ b/src/modules/tools/filamentdetector/FilamentDetector.cpp @@ -0,0 +1,246 @@ + +/* + Handles a filament detector that has an optical encoder wheel, that generates pulses as the filament + moves through it. + It also supports a "bulge" detector that triggers if the filament has a bulge in it +*/ + +#include "FilamentDetector.h" +#include "Kernel.h" +#include "Config.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "SlowTicker.h" +#include "PublicData.h" +#include "StreamOutputPool.h" +#include "StreamOutput.h" +#include "SerialMessage.h" +#include "FilamentDetector.h" +#include "utils.h" +#include "Gcode.h" +#include "ExtruderPublicAccess.h" + +#include "InterruptIn.h" // mbed +#include "us_ticker_api.h" // mbed + +#define extruder_checksum CHECKSUM("extruder") + +#define filament_detector_checksum CHECKSUM("filament_detector") +#define enable_checksum CHECKSUM("enable") +#define encoder_pin_checksum CHECKSUM("encoder_pin") +#define bulge_pin_checksum CHECKSUM("bulge_pin") +#define seconds_per_check_checksum CHECKSUM("seconds_per_check") +#define pulses_per_mm_checksum CHECKSUM("pulses_per_mm") + +FilamentDetector::FilamentDetector() +{ + suspended= false; + filament_out_alarm= false; + bulge_detected= false; + active= true; + e_last_moved= NAN; +} + +FilamentDetector::~FilamentDetector() +{ + if(encoder_pin != nullptr) delete encoder_pin; +} + +void FilamentDetector::on_module_loaded() +{ + // if the module is disabled -> do nothing + if(!THEKERNEL->config->value( filament_detector_checksum, enable_checksum )->by_default(false)->as_bool()) { + // as this module is not needed free up the resource + delete this; + return; + } + + // encoder pin has to be interrupt enabled pin like 0.26, 0.27, 0.28 + Pin dummy_pin; + dummy_pin.from_string( THEKERNEL->config->value(filament_detector_checksum, encoder_pin_checksum)->by_default("nc" )->as_string()); + this->encoder_pin= dummy_pin.interrupt_pin(); + + // optional bulge detector + bulge_pin.from_string( THEKERNEL->config->value(filament_detector_checksum, bulge_pin_checksum)->by_default("nc" )->as_string())->as_input(); + if(bulge_pin.connected()) { + // input pin polling + THEKERNEL->slow_ticker->attach( 100, this, &FilamentDetector::button_tick); + } + + //Valid configurations contain an encoder pin, a bulge pin or both. + //free the module if not a valid configuration + if(this->encoder_pin == nullptr && !bulge_pin.connected()) { + delete this; + return; + } + + //only monitor the encoder if we are using the encodeer. + if (this->encoder_pin != nullptr) { + // set interrupt on rising edge + this->encoder_pin->rise(this, &FilamentDetector::on_pin_rise); + NVIC_SetPriority(EINT3_IRQn, 16); // set to low priority + } + + + // how many seconds between checks, must be long enough for several pulses to be detected, but not too long + seconds_per_check= THEKERNEL->config->value(filament_detector_checksum, seconds_per_check_checksum)->by_default(2)->as_number(); + + // the number of pulses per mm of filament moving through the detector, can be fractional + pulses_per_mm= THEKERNEL->config->value(filament_detector_checksum, pulses_per_mm_checksum)->by_default(1)->as_number(); + + // register event-handlers + if (this->encoder_pin != nullptr) { + //This event is only valid if we are using the encodeer. + register_for_event(ON_SECOND_TICK); + } + + register_for_event(ON_MAIN_LOOP); + register_for_event(ON_CONSOLE_LINE_RECEIVED); + this->register_for_event(ON_GCODE_RECEIVED); +} + + +void FilamentDetector::send_command(std::string msg, StreamOutput *stream) +{ + struct SerialMessage message; + message.message = msg; + message.stream = stream; + THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message ); +} + +// needed to detect when we resume +void FilamentDetector::on_console_line_received( void *argument ) +{ + if(!suspended) return; + + SerialMessage new_message = *static_cast(argument); + string possible_command = new_message.message; + string cmd = shift_parameter(possible_command); + if(cmd == "resume" || cmd == "M601") { + this->pulses= 0; + e_last_moved= NAN; + suspended= false; + } +} + +float FilamentDetector::get_emove() +{ + pad_extruder_t rd; + if(PublicData::get_value( extruder_checksum, (void *)&rd )) return rd.current_position; + return NAN; +} + +void FilamentDetector::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + if (gcode->has_m) { + if (gcode->m == 404) { // set filament detector parameters S seconds per check, P pulses per mm + if(gcode->has_letter('S')){ + seconds_per_check= gcode->get_value('S'); + seconds_passed= 0; + } + if(gcode->has_letter('P')){ + pulses_per_mm= gcode->get_value('P'); + } + gcode->stream->printf("// pulses per mm: %f, seconds per check: %d\n", pulses_per_mm, seconds_per_check); + + } else if (gcode->m == 405) { // disable filament detector + active= false; + e_last_moved= get_emove(); + + }else if (gcode->m == 406) { // enable filament detector + this->pulses= 0; + e_last_moved= get_emove(); + active= true; + + }else if (gcode->m == 407) { // display filament detector pulses and status + float e_moved= get_emove(); + if(!isnan(e_moved)) { + float delta= e_moved - e_last_moved; + gcode->stream->printf("Extruder moved: %f mm\n", delta); + } + + gcode->stream->printf("Encoder pulses: %u\n", pulses.load()); + if(this->suspended) gcode->stream->printf("Filament detector triggered\n"); + gcode->stream->printf("Filament detector is %s\n", active?"enabled":"disabled"); + } + } +} + +void FilamentDetector::on_main_loop(void *argument) +{ + if (active && this->filament_out_alarm) { + this->filament_out_alarm = false; + if(bulge_detected){ + THEKERNEL->streams->printf("// Filament Detector has detected a bulge in the filament\n"); + bulge_detected= false; + }else{ + THEKERNEL->streams->printf("// Filament Detector has detected a filament jam\n"); + } + + if(!suspended) { + this->suspended= true; + // fire suspend command + this->send_command( "M600", &(StreamOutput::NullStream) ); + } + } +} + +void FilamentDetector::on_second_tick(void *argument) +{ + if(++seconds_passed >= seconds_per_check) { + seconds_passed= 0; + check_encoder(); + } +} + +// encoder pin interrupt +void FilamentDetector::on_pin_rise() +{ + this->pulses++; +} + +void FilamentDetector::check_encoder() +{ + if(suspended) return; // already suspended + if(!active) return; // not enabled + + uint32_t pulse_cnt= this->pulses.exchange(0); // atomic load and reset + + // get number of E steps taken and make sure we have seen enough pulses to cover that + float e_moved= get_emove(); + if(isnan(e_last_moved)) { + e_last_moved= e_moved; + return; + } + + float delta= e_moved - e_last_moved; + e_last_moved= e_moved; + if(delta < 0) { + // we ignore retracts for the purposes of jam detection + return; + } + + // figure out how many pulses need to have happened to cover that e move + uint32_t needed_pulses= floorf(delta*pulses_per_mm); + // NOTE if needed_pulses is 0 then extruder did not move since last check, or not enough to register + if(needed_pulses == 0) return; + + if(pulse_cnt == 0) { + // we got no pulses and E moved since last time so fire off alarm + this->filament_out_alarm= true; + } +} + +uint32_t FilamentDetector::button_tick(uint32_t dummy) +{ + if(!bulge_pin.connected() || suspended || !active) return 0; + + if(bulge_pin.get()) { + // we got a trigger from the bulge detector + this->filament_out_alarm= true; + this->bulge_detected= true; + } + + return 0; +} diff --git a/src/modules/tools/filamentdetector/FilamentDetector.h b/src/modules/tools/filamentdetector/FilamentDetector.h new file mode 100644 index 00000000..c5bdaa83 --- /dev/null +++ b/src/modules/tools/filamentdetector/FilamentDetector.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Module.h" +#include "Pin.h" + +#include +#include +#include + +namespace mbed { + class InterruptIn; +} + +class StreamOutput; + +class FilamentDetector: public Module +{ +public: + FilamentDetector(); + ~FilamentDetector(); + void on_module_loaded(); + void on_main_loop(void* argument); + void on_second_tick(void* argument); + void on_console_line_received( void *argument ); + void on_gcode_received(void *argument); + +private: + void on_pin_rise(); + void check_encoder(); + void send_command(std::string msg, StreamOutput *stream); + uint32_t button_tick(uint32_t dummy); + float get_emove(); + + mbed::InterruptIn *encoder_pin{0}; + Pin bulge_pin; + float e_last_moved{0}; + std::atomic_uint pulses{0}; + float pulses_per_mm{0}; + uint8_t seconds_per_check{1}; + uint8_t seconds_passed{0}; + + struct { + bool filament_out_alarm:1; + bool bulge_detected:1; + bool suspended:1; + bool active:1; + }; +}; diff --git a/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp b/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp new file mode 100644 index 00000000..70e1dc3c --- /dev/null +++ b/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp @@ -0,0 +1,123 @@ +#include "RotaryDeltaCalibration.h" +#include "EndstopsPublicAccess.h" +#include "Kernel.h" +#include "Robot.h" +#include "Config.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "StreamOutput.h" +#include "PublicDataRequest.h" +#include "PublicData.h" +#include "Gcode.h" +#include "StepperMotor.h" + +#define rotarydelta_checksum CHECKSUM("rotary_delta_calibration") +#define enable_checksum CHECKSUM("enable") + +void RotaryDeltaCalibration::on_module_loaded() +{ + // if the module is disabled -> do nothing + if(!THEKERNEL->config->value( rotarydelta_checksum, enable_checksum )->by_default(false)->as_bool()) { + // as this module is not needed free up the resource + delete this; + return; + } + + // register event-handlers + register_for_event(ON_GCODE_RECEIVED); +} + +float *RotaryDeltaCalibration::get_homing_offset() +{ + float *theta_offset; // points to theta offset in Endstop module + bool ok = PublicData::get_value( endstops_checksum, home_offset_checksum, &theta_offset ); + return ok ? theta_offset : nullptr; +} + +void RotaryDeltaCalibration::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + + if( gcode->has_m) { + switch( gcode->m ) { + case 206: { + float *theta_offset= get_homing_offset(); // points to theta offset in Endstop module + if (theta_offset == nullptr) { + gcode->stream->printf("error:no endstop module found\n"); + return; + } + + // set theta offset, set directly in the Endstop module (bad practice really) + if (gcode->has_letter('A')) theta_offset[0] = gcode->get_value('A'); + if (gcode->has_letter('B')) theta_offset[1] = gcode->get_value('B'); + if (gcode->has_letter('C')) theta_offset[2] = gcode->get_value('C'); + + gcode->stream->printf("Theta offset set: A %8.5f B %8.5f C %8.5f\n", theta_offset[0], theta_offset[1], theta_offset[2]); + + break; + } + + case 306: { + // for a rotary delta M306 calibrates the homing angle + // by doing M306 A-56.17 it will calculate the M206 A value (the theta offset for actuator A) based on the difference + // between what it thinks is the current angle and what the current angle actually is specified by A (ditto for B and C) + + ActuatorCoordinates current_angle; + // get the current angle for each actuator, NOTE we only deal with ABC so if there are more than 3 actuators this will probably go wonky + for (size_t i = 0; i < 3; i++) { + current_angle[i]= THEKERNEL->robot->actuators[i]->get_current_position(); + } + + if (gcode->has_letter('L') && gcode->get_value('L') != 0) { + // specifying L1 it will take the last probe position (set by G30 or G38.x ) and set the home offset based on that + // this allows the use of G30 to find the angle tool + uint8_t ok; + std::tie(current_angle[0], current_angle[1], current_angle[2], ok) = THEKERNEL->robot->get_last_probe_position(); + if(ok == 0) { + gcode->stream->printf("error:Nothing set as probe failed or not run\n"); + return; + } + } + + float *theta_offset= get_homing_offset(); // points to theta offset in Endstop module + if (theta_offset == nullptr) { + gcode->stream->printf("error:no endstop module found\n"); + return; + } + + int cnt= 0; + + //figure out what home_offset needs to be to correct the homing_position + if (gcode->has_letter('A')) { + float a = gcode->get_value('A'); // what actual angle is + theta_offset[0] -= (current_angle[0] - a); + current_angle[0]= a; + cnt++; + } + if (gcode->has_letter('B')) { + float b = gcode->get_value('B'); + theta_offset[1] -= (current_angle[1] - b); + current_angle[1]= b; + cnt++; + } + if (gcode->has_letter('C')) { + float c = gcode->get_value('C'); + theta_offset[2] -= (current_angle[2] - c); + current_angle[2]= c; + cnt++; + } + + // reset the actuator positions (and machine position accordingly) + // But only if all three actuators have been specified at the same time + if(cnt == 3 || (gcode->has_letter('R') && gcode->get_value('R') != 0)) { + THEKERNEL->robot->reset_actuator_position(current_angle); + gcode->stream->printf("NOTE: actuator position reset\n"); + }else{ + gcode->stream->printf("NOTE: actuator position NOT reset\n"); + } + + gcode->stream->printf("Theta Offset: A %8.5f B %8.5f C %8.5f\n", theta_offset[0], theta_offset[1], theta_offset[2]); + } + } + } +} diff --git a/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.h b/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.h new file mode 100644 index 00000000..1b4af195 --- /dev/null +++ b/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Module.h" + +class Gcode; + +class RotaryDeltaCalibration : public Module +{ +public: + RotaryDeltaCalibration(){}; + virtual ~RotaryDeltaCalibration(){}; + + void on_module_loaded(); + +private: + void on_gcode_received(void *argument); + float *get_homing_offset(); +}; diff --git a/src/modules/tools/scaracal/SCARAcal.cpp b/src/modules/tools/scaracal/SCARAcal.cpp new file mode 100644 index 00000000..326eedb1 --- /dev/null +++ b/src/modules/tools/scaracal/SCARAcal.cpp @@ -0,0 +1,291 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#include "SCARAcal.h" + +#include "Kernel.h" +#include "BaseSolution.h" +#include "Config.h" +#include "Robot.h" +#include "StepperMotor.h" +#include "StreamOutputPool.h" +#include "Gcode.h" +#include "Conveyor.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "SerialMessage.h" +#include "EndstopsPublicAccess.h" +#include "PublicData.h" +#include +#include + +#define scaracal_checksum CHECKSUM("scaracal") +#define enable_checksum CHECKSUM("enable") +#define slow_feedrate_checksum CHECKSUM("slow_feedrate") +#define z_move_checksum CHECKSUM("z_move") + +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 + +#define STEPPER THEKERNEL->robot->actuators +#define STEPS_PER_MM(a) (STEPPER[a]->get_steps_per_mm()) + +void SCARAcal::on_module_loaded() +{ + // if the module is disabled -> do nothing + if(!THEKERNEL->config->value( scaracal_checksum, enable_checksum )->by_default(false)->as_bool()) { + // as this module is not needed free up the resource + delete this; + return; + } + + // load settings + this->on_config_reload(this); + // register event-handlers + register_for_event(ON_GCODE_RECEIVED); +} + +void SCARAcal::on_config_reload(void *argument) +{ + this->slow_rate = THEKERNEL->config->value( scaracal_checksum, slow_feedrate_checksum )->by_default(5)->as_number(); // feedrate in mm/sec + this->z_move = THEKERNEL->config->value( scaracal_checksum, z_move_checksum )->by_default(0)->as_number(); // Optional movement of Z relative to the home position. + // positive values increase distance between nozzle and bed. + // negative will decrease. Useful when level code active to prevent collision + +} + + +// issue home command +void SCARAcal::home() +{ + Gcode gc("G28", &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc); +} + +bool SCARAcal::get_trim(float& x, float& y, float& z) +{ + void *returned_data; + bool ok = PublicData::get_value( endstops_checksum, trim_checksum, &returned_data ); + + if (ok) { + float *trim = static_cast(returned_data); + x= trim[0]; + y= trim[1]; + z= trim[2]; + return true; + } + return false; +} + +bool SCARAcal::set_trim(float x, float y, float z, StreamOutput *stream) +{ + float t[3]{x, y, z}; + bool ok= PublicData::set_value( endstops_checksum, trim_checksum, t); + + if (ok) { + stream->printf("set trim to X:%f Y:%f Z:%f\n", x, y, z); + } else { + stream->printf("unable to set trim, is endstops enabled?\n"); + } + + return ok; +} + +bool SCARAcal::get_home_offset(float& x, float& y, float& z) +{ + void *returned_data; + bool ok = PublicData::get_value( endstops_checksum, home_offset_checksum, &returned_data ); + + if (ok) { + float *home_offset = static_cast(returned_data); + x= home_offset[0]; + y= home_offset[1]; + z= home_offset[2]; + return true; + } + return false; +} + +bool SCARAcal::set_home_offset(float x, float y, float z, StreamOutput *stream) +{ + char cmd[64]; + + // Assemble Gcode to add onto the queue + snprintf(cmd, sizeof(cmd), "M206 X%1.3f Y%1.3f Z%1.3f", x, y, z); // Send saved Z homing offset + + Gcode gc(cmd, &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc); + + stream->printf("Set home_offset to X:%f Y:%f Z:%f\n", x, y, z); + + return true;//ok; +} + +bool SCARAcal::translate_trim(StreamOutput *stream) +{ + float S_trim[3], + home_off[3]; + ActuatorCoordinates actuator; + + this->get_home_offset(home_off[0], home_off[1], home_off[2]); // get home offsets + this->get_trim(S_trim[0], S_trim[1], S_trim[2]); // get current trim + + THEKERNEL->robot->arm_solution->cartesian_to_actuator( home_off, actuator ); // convert current home offset to actuator angles + + // Subtract trim values from actuators to determine the real home offset actuator position for X and Y. + + actuator[0] -= S_trim[0]; + actuator[1] -= S_trim[1]; + + // Clear X and Y trims internally + S_trim[0] = 0.0F; + S_trim[1] = 0.0F; + + // convert back to get the real cartesian home offsets + + THEKERNEL->robot->arm_solution->actuator_to_cartesian( actuator, home_off ); + + this->set_home_offset(home_off[0], home_off[1], home_off[2],stream); // get home offsets + // Set the correct home offsets; + + this->set_trim(S_trim[0], S_trim[1], S_trim[2], stream); // Now Clear relevant trims + + return true; +} + +void SCARAcal::SCARA_ang_move(float theta, float psi, float z, float feedrate) +{ + char cmd[64]; + float cartesian[3]; + ActuatorCoordinates actuator; + + // Assign the actuator angles from input + actuator[0] = theta; + actuator[1] = psi; + actuator[2] = z; + + // Calculate the physical position relating to the arm angles + THEKERNEL->robot->arm_solution->actuator_to_cartesian( actuator, cartesian ); + + // Assemble Gcode to add onto the queue + snprintf(cmd, sizeof(cmd), "G0 X%1.3f Y%1.3f Z%1.3f F%1.1f", cartesian[0], cartesian[1], cartesian[2], feedrate * 60); // use specified feedrate (mm/sec) + + //THEKERNEL->streams->printf("DEBUG: move: %s\n", cmd); + + Gcode gc(cmd, &(StreamOutput::NullStream)); + THEKERNEL->robot->on_gcode_received(&gc); // send to robot directly +} + +//A GCode has been received +//See if the current Gcode line has some orders for us +void SCARAcal::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + + if( gcode->has_m) { + switch( gcode->m ) { + + case 114: { // Extra stuff for Morgan calibration + char buf[32]; + float cartesian[3]; + ActuatorCoordinates actuators; + + THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position + + int n = snprintf(buf, sizeof(buf), " A: Th:%1.3f Ps:%1.3f", + actuators[0], + actuators[1]); // display actuator angles Theta and Psi. + gcode->txt_after_ok.append(buf, n); + } + break; + + case 360: { + float target[2] = {0.0F, 120.0F}, + cartesian[3], + S_trim[3]; + + this->get_trim(S_trim[0], S_trim[1], S_trim[2]); // get current trim to conserve other calbration values + + if(gcode->has_letter('P')) { + // Program the current position as target + ActuatorCoordinates actuators; + float S_delta[2], + S_trim[3]; + + THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position + + S_delta[0] = actuators[0] - target[0]; + + set_trim(S_delta[0], S_trim[1], S_trim[2], gcode->stream); + } else { + set_trim(0, S_trim[1], S_trim[2], gcode->stream); // reset trim for calibration move + this->home(); // home + THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + SCARA_ang_move(target[0], target[1], cartesian[2] + this->z_move, slow_rate * 3.0F); // move to target + } + } + break; + + case 361: { + float target[2] = {90.0F, 130.0F}, + cartesian[3]; + + if(gcode->has_letter('P')) { + // Program the current position as target + ActuatorCoordinates actuators; + + THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position + + STEPPER[0]->change_steps_per_mm(actuators[0] / target[0] * STEPPER[0]->get_steps_per_mm()); // Find angle difference + STEPPER[1]->change_steps_per_mm(STEPPER[0]->get_steps_per_mm()); // and change steps_per_mm to ensure correct steps per *angle* + } else { + this->home(); // home - This time leave trims as adjusted. + THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + SCARA_ang_move(target[0], target[1], cartesian[2] + this->z_move, slow_rate * 3.0F); // move to target + } + + } + break; + + case 364: { + float target[2] = {45.0F, 135.0F}, + cartesian[3], + S_trim[3]; + + this->get_trim(S_trim[0], S_trim[1], S_trim[2]); // get current trim to conserve other calbration values + + if(gcode->has_letter('P')) { + // Program the current position as target + ActuatorCoordinates actuators; + float S_delta[2]; + + THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate it to get actual actuator angles + + S_delta[1] = ( actuators[1] - actuators[0]) - ( target[1] - target[0] ); // Find difference in angle - not actuator difference, and + set_trim(S_trim[0], S_delta[1], S_trim[2], gcode->stream); // set trim to reflect the difference + } else { + set_trim(S_trim[0], 0, S_trim[2], gcode->stream); // reset trim for calibration move + this->home(); // home + THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + SCARA_ang_move(target[0], target[1], cartesian[2] + this->z_move, slow_rate * 3.0F); // move to target + } + } + break; + + case 366: // Translate trims to the actual endstop offsets for SCARA + this->translate_trim(gcode->stream); + break; + + } + } +} + diff --git a/src/modules/tools/scaracal/SCARAcal.h b/src/modules/tools/scaracal/SCARAcal.h new file mode 100644 index 00000000..50d4fff7 --- /dev/null +++ b/src/modules/tools/scaracal/SCARAcal.h @@ -0,0 +1,48 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#ifndef SCARACAL_H_ +#define SCARACAL_H_ + +#include "Module.h" +#include "Pin.h" + +class StepperMotor; +class Gcode; +class StreamOutput; + +class SCARAcal: public Module +{ + +public: + void on_module_loaded(); + void on_config_reload(void *argument); + + void on_gcode_received(void *argument); + + +private: + void home(); + bool set_trim(float x, float y, float z, StreamOutput *stream); + bool get_trim(float& x, float& y, float& z); + + bool set_home_offset(float x, float y, float z, StreamOutput *stream); + bool get_home_offset(float& x, float& y, float& z); + + bool translate_trim(StreamOutput *stream); + + void SCARA_ang_move(float theta, float psi, float z, float feedrate); + + float slow_rate; + float z_move; + + struct { + bool is_scara:1; + }; +}; + +#endif /* SCARACAL_H_ */ diff --git a/src/modules/tools/spindle/Spindle.cpp b/src/modules/tools/spindle/Spindle.cpp new file mode 100644 index 00000000..9267f43c --- /dev/null +++ b/src/modules/tools/spindle/Spindle.cpp @@ -0,0 +1,210 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "Spindle.h" +#include "Config.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "Gcode.h" +#include "StreamOutputPool.h" +#include "SlowTicker.h" +#include "Conveyor.h" +//#include "system_LPC17xx.h" +#include "utils.h" + +#include "libs/Pin.h" +#include "mbed.h" +#include "PwmOut.h" +#include "port_api.h" +#include "us_ticker_api.h" + +#define spindle_enable_checksum CHECKSUM("spindle_enable") +#define spindle_pwm_pin_checksum CHECKSUM("spindle_pwm_pin") +#define spindle_pwm_period_checksum CHECKSUM("spindle_pwm_period") +#define spindle_feedback_pin_checksum CHECKSUM("spindle_feedback_pin") +#define spindle_pulses_per_rev_checksum CHECKSUM("spindle_pulses_per_rev") +#define spindle_default_rpm_checksum CHECKSUM("spindle_default_rpm") +#define spindle_control_P_checksum CHECKSUM("spindle_control_P") +#define spindle_control_I_checksum CHECKSUM("spindle_control_I") +#define spindle_control_D_checksum CHECKSUM("spindle_control_D") +#define spindle_control_smoothing_checksum CHECKSUM("spindle_control_smoothing") + +#define UPDATE_FREQ 1000 + +Spindle::Spindle() +{ +} + +void Spindle::on_module_loaded() +{ + last_time = 0; + last_edge = 0; + current_rpm = 0; + current_I_value = 0; + current_pwm_value = 0; + time_since_update = 0; + spindle_on = true; + + if (!THEKERNEL->config->value(spindle_enable_checksum)->by_default(false)->as_bool()) { + delete this; // Spindle control module is disabled + return; + } + + pulses_per_rev = THEKERNEL->config->value(spindle_pulses_per_rev_checksum)->by_default(1.0f)->as_number(); + target_rpm = THEKERNEL->config->value(spindle_default_rpm_checksum)->by_default(5000.0f)->as_number(); + control_P_term = THEKERNEL->config->value(spindle_control_P_checksum)->by_default(0.0001f)->as_number(); + control_I_term = THEKERNEL->config->value(spindle_control_I_checksum)->by_default(0.0001f)->as_number(); + control_D_term = THEKERNEL->config->value(spindle_control_D_checksum)->by_default(0.0001f)->as_number(); + + // Smoothing value is low pass filter time constant in seconds. + float smoothing_time = THEKERNEL->config->value(spindle_control_smoothing_checksum)->by_default(0.1f)->as_number(); + if (smoothing_time * UPDATE_FREQ < 1.0f) + smoothing_decay = 1.0f; + else + smoothing_decay = 1.0f / (UPDATE_FREQ * smoothing_time); + + // Get the pin for hardware pwm + { + Pin *smoothie_pin = new Pin(); + smoothie_pin->from_string(THEKERNEL->config->value(spindle_pwm_pin_checksum)->by_default("nc")->as_string()); + spindle_pin = smoothie_pin->as_output()->hardware_pwm(); + output_inverted = smoothie_pin->is_inverting(); + delete smoothie_pin; + } + + if (spindle_pin == NULL) { + THEKERNEL->streams->printf("Error: Spindle PWM pin must be P2.0-2.5 or other PWM pin\n"); + delete this; + return; + } + + int period = THEKERNEL->config->value(spindle_pwm_period_checksum)->by_default(1000)->as_int(); + spindle_pin->period_us(period); + spindle_pin->write(output_inverted ? 1 : 0); + + // Get the pin for interrupt + { + Pin *smoothie_pin = new Pin(); + smoothie_pin->from_string(THEKERNEL->config->value(spindle_feedback_pin_checksum)->by_default("nc")->as_string()); + smoothie_pin->as_input(); + if (smoothie_pin->port_number == 0 || smoothie_pin->port_number == 2) { + PinName pinname = port_pin((PortName)smoothie_pin->port_number, smoothie_pin->pin_number); + feedback_pin = new InterruptIn(pinname); + //feedback_pin.rise(&on_pin_rise); // TODO need to assign interrupt routine + } else { + THEKERNEL->streams->printf("Error: Spindle feedback pin has to be on P0 or P2.\n"); + delete this; + return; + } + delete smoothie_pin; + } + + THEKERNEL->slow_ticker->attach(UPDATE_FREQ, this, &Spindle::on_update_speed); + register_for_event(ON_GCODE_RECEIVED); +} + +void Spindle::on_pin_rise() +{ + uint32_t timestamp = us_ticker_read(); + last_time = timestamp - last_edge; + last_edge = timestamp; + irq_count++; +} + +uint32_t Spindle::on_update_speed(uint32_t dummy) +{ + // If we don't get any interrupts for 1 second, set current RPM to 0 + uint32_t new_irq = irq_count; + if (last_irq != new_irq) + time_since_update = 0; + else + time_since_update++; + last_irq = new_irq; + + if (time_since_update > UPDATE_FREQ) + last_time = 0; + + // Calculate current RPM + uint32_t t = last_time; + if (t == 0) { + current_rpm = 0; + } else { + float new_rpm = 1000000 * 60.0f / (t * pulses_per_rev); + current_rpm = smoothing_decay * new_rpm + (1.0f - smoothing_decay) * current_rpm; + } + + if (spindle_on) { + float error = target_rpm - current_rpm; + + current_I_value += control_I_term * error * 1.0f / UPDATE_FREQ; + current_I_value = confine(current_I_value, -1.0f, 1.0f); + + float new_pwm = 0.5f; + new_pwm += control_P_term * error; + new_pwm += current_I_value; + new_pwm += control_D_term * UPDATE_FREQ * (error - prev_error); + new_pwm = confine(new_pwm, 0.0f, 1.0f); + prev_error = error; + + current_pwm_value = new_pwm; + } else { + current_I_value = 0; + current_pwm_value = 0; + } + + if (output_inverted) + spindle_pin->write(1.0f - current_pwm_value); + else + spindle_pin->write(current_pwm_value); + + return 0; +} + + +void Spindle::on_gcode_received(void* argument) +{ + Gcode *gcode = static_cast(argument); + + if (gcode->has_m) { + if (gcode->m == 957) { + // M957: report spindle speed + THEKERNEL->streams->printf("Current RPM: %5.0f Target RPM: %5.0f PWM value: %5.3f\n", + current_rpm, target_rpm, current_pwm_value); + } else if (gcode->m == 958) { + // M958: set spindle PID parameters + if (gcode->has_letter('P')) + control_P_term = gcode->get_value('P'); + if (gcode->has_letter('I')) + control_I_term = gcode->get_value('I'); + if (gcode->has_letter('D')) + control_D_term = gcode->get_value('D'); + THEKERNEL->streams->printf("P: %0.6f I: %0.6f D: %0.6f\n", + control_P_term, control_I_term, control_D_term); + } else if (gcode->m == 3) { + THEKERNEL->conveyor->wait_for_empty_queue(); + + // M3: Spindle on + spindle_on = true; + + if (gcode->has_letter('S')) { + target_rpm = gcode->get_value('S'); + } + + } else if (gcode->m == 5) { + THEKERNEL->conveyor->wait_for_empty_queue(); + spindle_on = false; + } + + }else if(!gcode->has_m && !gcode->has_g && gcode->has_letter('S')) { + // special case S on its own + THEKERNEL->conveyor->wait_for_empty_queue(); + target_rpm = gcode->get_value('S'); + } +} + diff --git a/src/modules/tools/spindle/Spindle.h b/src/modules/tools/spindle/Spindle.h new file mode 100644 index 00000000..d28b8726 --- /dev/null +++ b/src/modules/tools/spindle/Spindle.h @@ -0,0 +1,60 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#ifndef SPINDLE_MODULE_H +#define SPINDLE_MODULE_H + +#include "libs/Module.h" +#include + +namespace mbed { + class PwmOut; + class InterruptIn; +} + +// This module implements closed loop PID control for spindle RPM. +class Spindle: public Module { + public: + Spindle(); + virtual ~Spindle() {}; + void on_module_loaded(); + + + private: + void on_pin_rise(); + void on_gcode_received(void *argument); + uint32_t on_update_speed(uint32_t dummy); + + mbed::PwmOut *spindle_pin; // PWM output for spindle speed control + mbed::InterruptIn *feedback_pin; // Interrupt pin for measuring speed + bool output_inverted; + + // Current values, updated at runtime + bool spindle_on; + float current_rpm; + float target_rpm; + float current_I_value; + float prev_error; + float current_pwm_value; + int time_since_update; + uint32_t last_irq; + + // Values from config + float pulses_per_rev; + float control_P_term; + float control_I_term; + float control_D_term; + float smoothing_decay; + + // These fields are updated by the interrupt + uint32_t last_edge; // Timestamp of last edge + volatile uint32_t last_time; // Time delay between last two edges + volatile uint32_t irq_count; +}; + +#endif + diff --git a/src/modules/tools/temperatureswitch/TemperatureSwitch.cpp b/src/modules/tools/temperatureswitch/TemperatureSwitch.cpp new file mode 100755 index 00000000..510da512 --- /dev/null +++ b/src/modules/tools/temperatureswitch/TemperatureSwitch.cpp @@ -0,0 +1,241 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +/* +TemperatureSwitch is an optional module that will automatically turn on or off a switch +based on a setpoint temperature. It is commonly used to turn on/off a cooling fan or water pump +to cool the hot end's cold zone. Specifically, it turns one of the small MOSFETs on or off. + +Author: Michael Hackney, mhackney@eclecticangler.com +*/ + +#include "TemperatureSwitch.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h" +#include "SwitchPublicAccess.h" + +#include "utils.h" +#include "Gcode.h" +#include "Config.h" +#include "ConfigValue.h" +#include "checksumm.h" +#include "PublicData.h" +#include "StreamOutputPool.h" +#include "TemperatureControlPool.h" +#include "mri.h" + +#define temperatureswitch_checksum CHECKSUM("temperatureswitch") +#define enable_checksum CHECKSUM("enable") +#define temperatureswitch_hotend_checksum CHECKSUM("hotend") +#define temperatureswitch_threshold_temp_checksum CHECKSUM("threshold_temp") +#define temperatureswitch_type_checksum CHECKSUM("type") +#define temperatureswitch_switch_checksum CHECKSUM("switch") +#define temperatureswitch_heatup_poll_checksum CHECKSUM("heatup_poll") +#define temperatureswitch_cooldown_poll_checksum CHECKSUM("cooldown_poll") +#define temperatureswitch_trigger_checksum CHECKSUM("trigger") +#define temperatureswitch_inverted_checksum CHECKSUM("inverted") +#define temperatureswitch_arm_command_checksum CHECKSUM("arm_mcode") +#define designator_checksum CHECKSUM("designator") + +TemperatureSwitch::TemperatureSwitch() +{ +} + +TemperatureSwitch::~TemperatureSwitch() +{ + THEKERNEL->unregister_for_event(ON_SECOND_TICK, this); + THEKERNEL->unregister_for_event(ON_GCODE_RECEIVED, this); +} + +// Load module +void TemperatureSwitch::on_module_loaded() +{ + vector modulist; + // allow for multiple temperature switches + THEKERNEL->config->get_module_list(&modulist, temperatureswitch_checksum); + for (auto m : modulist) { + load_config(m); + } + + // no longer need this instance as it is just used to load the other instances + delete this; +} + +TemperatureSwitch* TemperatureSwitch::load_config(uint16_t modcs) +{ + // see if enabled + if (!THEKERNEL->config->value(temperatureswitch_checksum, modcs, enable_checksum)->by_default(false)->as_bool()) { + return nullptr; + } + + // create a temperature control and load settings + char designator= 0; + string s= THEKERNEL->config->value(temperatureswitch_checksum, modcs, designator_checksum)->by_default("")->as_string(); + if(s.empty()){ + // for backward compatibility temperatureswitch.hotend will need designator 'T' by default @DEPRECATED + if(modcs == temperatureswitch_hotend_checksum) designator= 'T'; + + }else{ + designator= s[0]; + } + + if(designator == 0) return nullptr; // no designator then not valid + + // load settings from config file + string switchname = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_switch_checksum)->by_default("")->as_string(); + if(switchname.empty()) { + // handle old configs where this was called type @DEPRECATED + switchname = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_type_checksum)->by_default("")->as_string(); + if(switchname.empty()) { + // no switch specified so invalid entry + THEKERNEL->streams->printf("WARNING TEMPERATURESWITCH: no switch specified\n"); + return nullptr; + } + } + + // create a new temperature switch module + TemperatureSwitch *ts= new TemperatureSwitch(); + + // save designator + ts->designator= designator; + + // if we should turn the switch on or off when trigger is hit + ts->inverted = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_inverted_checksum)->by_default(false)->as_bool(); + + // if we should trigger when above and below, or when rising through, or when falling through the specified temp + string trig = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_trigger_checksum)->by_default("level")->as_string(); + if(trig == "level") ts->trigger= LEVEL; + else if(trig == "rising") ts->trigger= RISING; + else if(trig == "falling") ts->trigger= FALLING; + else ts->trigger= LEVEL; + + // the mcode used to arm the switch + ts->arm_mcode = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_arm_command_checksum)->by_default(0)->as_number(); + + ts->temperatureswitch_switch_cs= get_checksum(switchname); // checksum of the switch to use + + ts->temperatureswitch_threshold_temp = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_threshold_temp_checksum)->by_default(50.0f)->as_number(); + + // these are to tune the heatup and cooldown polling frequencies + ts->temperatureswitch_heatup_poll = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_heatup_poll_checksum)->by_default(15)->as_number(); + ts->temperatureswitch_cooldown_poll = THEKERNEL->config->value(temperatureswitch_checksum, modcs, temperatureswitch_cooldown_poll_checksum)->by_default(60)->as_number(); + ts->current_delay = ts->temperatureswitch_heatup_poll; + + // set initial state + ts->current_state= NONE; + ts->second_counter = ts->current_delay; // do test immediately on first second_tick + // if not defined then always armed, otherwise start out disarmed + ts->armed= (ts->arm_mcode == 0); + + // Register for events + ts->register_for_event(ON_SECOND_TICK); + + if(ts->arm_mcode != 0) { + ts->register_for_event(ON_GCODE_RECEIVED); + } + return ts; +} + +void TemperatureSwitch::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + if(gcode->has_m && gcode->m == this->arm_mcode) { + this->armed= (gcode->has_letter('S') && gcode->get_value('S') != 0); + gcode->stream->printf("temperature switch %s\n", this->armed ? "armed" : "disarmed"); + } +} + +// Called once a second but we only need to service on the cooldown and heatup poll intervals +void TemperatureSwitch::on_second_tick(void *argument) +{ + second_counter++; + if (second_counter < current_delay) return; + + second_counter = 0; + float current_temp = this->get_highest_temperature(); + + if (current_temp >= this->temperatureswitch_threshold_temp) { + set_state(HIGH_TEMP); + + } else { + set_state(LOW_TEMP); + } +} + +void TemperatureSwitch::set_state(STATE state) +{ + if(state == this->current_state) return; // state did not change + + // state has changed + switch(this->trigger) { + case LEVEL: + // switch on or off depending on HIGH or LOW + set_switch(state == HIGH_TEMP); + break; + + case RISING: + // switch on if rising edge + if(this->current_state == LOW_TEMP && state == HIGH_TEMP) set_switch(true); + break; + + case FALLING: + // switch off if falling edge + if(this->current_state == HIGH_TEMP && state == LOW_TEMP) set_switch(false); + break; + } + + this->current_delay = state == HIGH_TEMP ? this->temperatureswitch_cooldown_poll : this->temperatureswitch_heatup_poll; + this->current_state= state; +} + +// Get the highest temperature from the set of temperature controllers +float TemperatureSwitch::get_highest_temperature() +{ + float high_temp = 0.0; + + std::vector controllers; + bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers); + if (ok) { + for (auto &c : controllers) { + // check if this controller's temp is the highest and save it if so + if (c.designator[0] == this->designator && c.current_temperature > high_temp) { + high_temp = c.current_temperature; + } + } + } + + return high_temp; +} + +// Turn the switch on (true) or off (false) +void TemperatureSwitch::set_switch(bool switch_state) +{ + if(!this->armed) return; // do not actually switch anything if not armed + + if(this->arm_mcode != 0 && this->trigger != LEVEL) { + // if edge triggered we only trigger once per arming, if level triggered we switch as long as we are armed + this->armed= false; + } + + if(this->inverted) switch_state= !switch_state; // turn switch on or off inverted + + // get current switch state + struct pad_switch pad; + bool ok = PublicData::get_value(switch_checksum, this->temperatureswitch_switch_cs, 0, &pad); + if (!ok) { + THEKERNEL->streams->printf("// Failed to get switch state.\r\n"); + return; + } + + if(pad.state == switch_state) return; // switch is already in the requested state + + ok = PublicData::set_value(switch_checksum, this->temperatureswitch_switch_cs, state_checksum, &switch_state); + if (!ok) { + THEKERNEL->streams->printf("// Failed changing switch state.\r\n"); + } +} diff --git a/src/modules/tools/temperatureswitch/TemperatureSwitch.h b/src/modules/tools/temperatureswitch/TemperatureSwitch.h new file mode 100755 index 00000000..1c45c90d --- /dev/null +++ b/src/modules/tools/temperatureswitch/TemperatureSwitch.h @@ -0,0 +1,82 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +/* +TemperatureSwitch is an optional module that will automatically turn on or off a switch +based on a setpoint temperature. It is commonly used to turn on/off a cooling fan or water pump +to cool the hot end's cold zone. Specifically, it turns one of the small MOSFETs on or off. + +Author: Michael Hackney, mhackney@eclecticangler.com +*/ + +#ifndef TEMPERATURESWITCH_MODULE_H +#define TEMPERATURESWITCH_MODULE_H + +using namespace std; + +#include "libs/Module.h" +#include +#include + +class TemperatureSwitch : public Module +{ + public: + TemperatureSwitch(); + ~TemperatureSwitch(); + void on_module_loaded(); + void on_second_tick(void *argument); + void on_gcode_received(void *argument); + TemperatureSwitch* load_config(uint16_t modcs); + + bool is_armed() const { return armed; } + + private: + enum TRIGGER_TYPE {LEVEL, RISING, FALLING}; + enum STATE {NONE, HIGH_TEMP, LOW_TEMP}; + + // get the highest temperature from the set of configured temperature controllers + float get_highest_temperature(); + + // turn the switch on or off + void set_switch(bool cooler_state); + + // temperature has changed state + void set_state(STATE state); + + // temperatureswitch.hotend.threshold_temp + float temperatureswitch_threshold_temp; + + // temperatureswitch.hotend.switch + uint16_t temperatureswitch_switch_cs; + + // check temps on heatup every X seconds + // this can be set in config: temperatureswitch.hotend.heatup_poll + uint16_t temperatureswitch_heatup_poll; + + // check temps on cooldown every X seconds + // this can be set in config: temperatureswitch.hotend.cooldown_poll + uint16_t temperatureswitch_cooldown_poll; + + // our internal second counter + uint16_t second_counter; + + // we are delaying for this many seconds + uint16_t current_delay; + + // the mcode that will arm the switch, 0 means always armed + uint16_t arm_mcode; + + struct { + char designator:8; + bool inverted:1; + bool armed:1; + TRIGGER_TYPE trigger:2; + STATE current_state:2; + }; +}; + +#endif diff --git a/src/modules/utils/PlayLed/PlayLed.cpp b/src/modules/utils/PlayLed/PlayLed.cpp new file mode 100644 index 00000000..af06db91 --- /dev/null +++ b/src/modules/utils/PlayLed/PlayLed.cpp @@ -0,0 +1,60 @@ +#include "PlayLed.h" + +/* + * LED indicator: + * off = not paused, nothing to do + * fast flash = halted + * on = a block is being executed + */ + +#include "modules/robot/Conveyor.h" +#include "SlowTicker.h" +#include "Config.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "Gcode.h" + +#define pause_led_pin_checksum CHECKSUM("pause_led_pin") +#define play_led_pin_checksum CHECKSUM("play_led_pin") +#define play_led_disable_checksum CHECKSUM("play_led_disable") + +PlayLed::PlayLed() { + cnt= 0; +} + +void PlayLed::on_module_loaded() +{ + if(THEKERNEL->config->value( play_led_disable_checksum )->by_default(false)->as_bool()) { + delete this; + return; + } + + on_config_reload(this); + + THEKERNEL->slow_ticker->attach(12, this, &PlayLed::led_tick); +} + +void PlayLed::on_config_reload(void *argument) +{ + string ledpin = "4.28!"; + + ledpin = THEKERNEL->config->value( pause_led_pin_checksum )->by_default(ledpin)->as_string(); // check for pause_led_pin first + ledpin = THEKERNEL->config->value( play_led_pin_checksum )->by_default(ledpin)->as_string(); // override with play_led_pin if it's found + + led.from_string(ledpin)->as_output()->set(false); +} + +uint32_t PlayLed::led_tick(uint32_t) +{ + if(THEKERNEL->is_halted()) { + led.set(!led.get()); + return 0; + } + + if(++cnt >= 6) { // 6 ticks ~ 500ms + cnt= 0; + led.set(!THEKERNEL->conveyor->is_queue_empty()); + } + + return 0; +} diff --git a/src/modules/utils/PlayLed/PlayLed.h b/src/modules/utils/PlayLed/PlayLed.h new file mode 100644 index 00000000..d139d8c9 --- /dev/null +++ b/src/modules/utils/PlayLed/PlayLed.h @@ -0,0 +1,24 @@ +#ifndef _PLAYLED_H +#define _PLAYLED_H + +#include "libs/Kernel.h" +#include "libs/Pin.h" + + +class PlayLed : public Module +{ +public: + PlayLed(); + + void on_module_loaded(void); + void on_config_reload(void *); + +private: + uint32_t led_tick(uint32_t); + Pin led; + struct { + uint8_t cnt:4; + }; +}; + +#endif /* _PLAYLED_H */ diff --git a/src/modules/utils/configurator/Configurator.cpp b/src/modules/utils/configurator/Configurator.cpp new file mode 100644 index 00000000..f6601fb1 --- /dev/null +++ b/src/modules/utils/configurator/Configurator.cpp @@ -0,0 +1,132 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + + +#include "libs/Kernel.h" +#include "Configurator.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/SerialMessage.h" +#include "libs/StreamOutput.h" +#include "checksumm.h" +#include "Config.h" +#include "Gcode.h" +#include "ConfigSource.h" +#include "FileConfigSource.h" +#include "ConfigValue.h" +#include "ConfigCache.h" + +#define CONF_NONE 0 +#define CONF_ROM 1 +#define CONF_SD 2 +#define CONF_EEPROM 3 + + +// Output a ConfigValue from the specified ConfigSource to the stream +void Configurator::config_get_command( string parameters, StreamOutput *stream ) +{ + string source = shift_parameter(parameters); + string setting = shift_parameter(parameters); + if (setting == "") { // output settings from the config-cache + setting = source; + source = ""; + uint16_t setting_checksums[3]; + get_checksums(setting_checksums, setting ); + THEKERNEL->config->config_cache_load(); // need to load config cache first as it is unloaded after booting + ConfigValue *cv = THEKERNEL->config->value(setting_checksums); + if(cv != NULL && cv->found) { + string value = cv->as_string(); + stream->printf( "cached: %s is set to %s\r\n", setting.c_str(), value.c_str() ); + } else { + stream->printf( "cached: %s is not in config\r\n", setting.c_str()); + } + THEKERNEL->config->config_cache_clear(); + + } else { // output setting from specified source by parsing the config file + uint16_t source_checksum = get_checksum( source ); + uint16_t setting_checksums[3]; + get_checksums(setting_checksums, setting ); + for(unsigned int i = 0; i < THEKERNEL->config->config_sources.size(); i++) { + if( THEKERNEL->config->config_sources[i]->is_named(source_checksum) ) { + string value = THEKERNEL->config->config_sources[i]->read(setting_checksums); + if(value.empty()) { + stream->printf( "%s: %s is not in config\r\n", source.c_str(), setting.c_str() ); + } else { + stream->printf( "%s: %s is set to %s\r\n", source.c_str(), setting.c_str(), value.c_str() ); + } + break; + } + } + } +} + +// Write the specified setting to the specified ConfigSource +void Configurator::config_set_command( string parameters, StreamOutput *stream ) +{ + string source = shift_parameter(parameters); + string setting = shift_parameter(parameters); + string value = shift_parameter(parameters); + if(source.empty() || setting.empty() || value.empty()) { + stream->printf( "Usage: config-set source setting value # where source is sd, setting is the key and value is the new value\r\n" ); + return; + } + + uint16_t source_checksum = get_checksum(source); + for(unsigned int i = 0; i < THEKERNEL->config->config_sources.size(); i++) { + if( THEKERNEL->config->config_sources[i]->is_named(source_checksum) ) { + if(THEKERNEL->config->config_sources[i]->write(setting, value)) { + stream->printf( "%s: %s has been set to %s\r\n", source.c_str(), setting.c_str(), value.c_str() ); + } else { + stream->printf( "%s: %s not enough space to overwrite existing key/value\r\n", source.c_str(), setting.c_str() ); + } + return; + } + } + stream->printf( "%s source does not exist\r\n", source.c_str()); + + /* Live setting not really supported anymore as the cache is never left loaded + if (value == "") { + if(!THEKERNEL->config->config_cache_loaded) { + stream->printf( "live: setting not allowed as config cache is not loaded\r\n" ); + return; + } + value = setting; + setting = source; + source = ""; + THEKERNEL->config->set_string(setting, value); + stream->printf( "live: %s has been set to %s\r\n", setting.c_str(), value.c_str() ); + */ +} + +// Reload config values from the specified ConfigSource, NOTE used for debugging by dumping the config-cache +void Configurator::config_load_command( string parameters, StreamOutput *stream ) +{ + string source = shift_parameter(parameters); + if(source == "load") { + THEKERNEL->config->config_cache_load(); + stream->printf( "config cache loaded\r\n"); + + } else if(source == "unload") { + THEKERNEL->config->config_cache_clear(); + stream->printf( "config cache unloaded\r\n" ); + + } else if(source == "dump") { + THEKERNEL->config->config_cache_load(); + THEKERNEL->config->config_cache->dump(stream); + THEKERNEL->config->config_cache_clear(); + + } else if(source == "checksum") { + string key = shift_parameter(parameters); + uint16_t cs[3]; + get_checksums(cs, key); + stream->printf( "checksum of %s = %02X %02X %02X\n", key.c_str(), cs[0], cs[1], cs[2]); + + } else { + stream->printf( "unsupported option: must be one of load|unload|dump|checksum\n" ); + } +} + diff --git a/src/modules/utils/configurator/Configurator.h b/src/modules/utils/configurator/Configurator.h new file mode 100644 index 00000000..3752fd2c --- /dev/null +++ b/src/modules/utils/configurator/Configurator.h @@ -0,0 +1,28 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + + +#ifndef configurator_h +#define configurator_h + +#include +using std::string; + +class StreamOutput; + +class Configurator +{ +public: + Configurator() {} + + void config_get_command( string parameters, StreamOutput *stream ); + void config_set_command( string parameters, StreamOutput *stream ); + void config_load_command(string parameters, StreamOutput *stream ); +}; + + +#endif // configurator_h diff --git a/src/modules/utils/currentcontrol/CurrentControl.cpp b/src/modules/utils/currentcontrol/CurrentControl.cpp new file mode 100644 index 00000000..2e30f3b0 --- /dev/null +++ b/src/modules/utils/currentcontrol/CurrentControl.cpp @@ -0,0 +1,110 @@ +#include "CurrentControl.h" +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "ConfigValue.h" +#include "libs/StreamOutput.h" + +#include "Gcode.h" +#include "Config.h" +#include "checksumm.h" +#include "DigipotBase.h" + +// add new digipot chips here +#include "mcp4451.h" +#include "ad5206.h" + +#include +using namespace std; + +#define alpha_current_checksum CHECKSUM("alpha_current") +#define beta_current_checksum CHECKSUM("beta_current") +#define gamma_current_checksum CHECKSUM("gamma_current") +#define delta_current_checksum CHECKSUM("delta_current") +#define epsilon_current_checksum CHECKSUM("epsilon_current") +#define zeta_current_checksum CHECKSUM("zeta_current") +#define eta_current_checksum CHECKSUM("eta_current") +#define theta_current_checksum CHECKSUM("theta_current") +#define currentcontrol_module_enable_checksum CHECKSUM("currentcontrol_module_enable") +#define digipotchip_checksum CHECKSUM("digipotchip") +#define digipot_max_current CHECKSUM("digipot_max_current") +#define digipot_factor CHECKSUM("digipot_factor") + +#define mcp4451_checksum CHECKSUM("mcp4451") +#define ad5206_checksum CHECKSUM("ad5206") + +CurrentControl::CurrentControl() +{ + digipot = NULL; +} + +void CurrentControl::on_module_loaded() +{ + if( !THEKERNEL->config->value( currentcontrol_module_enable_checksum )->by_default(false)->as_bool() ) { + // as this module is not needed free up the resource + delete this; + return; + } + + // allocate digipot, if already allocated delete it first + delete digipot; + + // see which chip to use + int chip_checksum = get_checksum(THEKERNEL->config->value(digipotchip_checksum)->by_default("mcp4451")->as_string()); + if(chip_checksum == mcp4451_checksum) { + digipot = new MCP4451(); + } else if(chip_checksum == ad5206_checksum) { + digipot = new AD5206(); + } else { // need a default so use smoothie + digipot = new MCP4451(); + } + + digipot->set_max_current( THEKERNEL->config->value(digipot_max_current )->by_default(2.0f)->as_number()); + digipot->set_factor( THEKERNEL->config->value(digipot_factor )->by_default(113.33f)->as_number()); + + // Get configuration + this->digipot->set_current(0, THEKERNEL->config->value(alpha_current_checksum )->by_default(0.8f)->as_number()); + this->digipot->set_current(1, THEKERNEL->config->value(beta_current_checksum )->by_default(0.8f)->as_number()); + this->digipot->set_current(2, THEKERNEL->config->value(gamma_current_checksum )->by_default(0.8f)->as_number()); + this->digipot->set_current(3, THEKERNEL->config->value(delta_current_checksum )->by_default(0.8f)->as_number()); + this->digipot->set_current(4, THEKERNEL->config->value(epsilon_current_checksum)->by_default(-1)->as_number()); + this->digipot->set_current(5, THEKERNEL->config->value(zeta_current_checksum )->by_default(-1)->as_number()); + this->digipot->set_current(6, THEKERNEL->config->value(eta_current_checksum )->by_default(-1)->as_number()); + this->digipot->set_current(7, THEKERNEL->config->value(theta_current_checksum )->by_default(-1)->as_number()); + + + this->register_for_event(ON_GCODE_RECEIVED); +} + + +void CurrentControl::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + char alpha[8] = { 'X', 'Y', 'Z', 'E', 'A', 'B', 'C', 'D' }; + if (gcode->has_m) { + if (gcode->m == 907) { + for (int i = 0; i < 8; i++) { + if (gcode->has_letter(alpha[i])) { + float c = gcode->get_value(alpha[i]); + this->digipot->set_current(i, c); + } + } + + } else if(gcode->m == 500 || gcode->m == 503) { + float currents[8]; + bool has_setting= false; + for (int i = 0; i < 8; i++) { + currents[i]= this->digipot->get_current(i); + if(currents[i] >= 0) has_setting= true; + } + if(!has_setting) return; // don't oupuit anything if none are set using this current control + + gcode->stream->printf(";Digipot Motor currents:\nM907 "); + for (int i = 0; i < 8; i++) { + if(currents[i] >= 0) + gcode->stream->printf("%c%1.5f ", alpha[i], currents[i]); + } + gcode->stream->printf("\n"); + } + } +} diff --git a/src/modules/utils/currentcontrol/CurrentControl.h b/src/modules/utils/currentcontrol/CurrentControl.h new file mode 100644 index 00000000..a7269c7a --- /dev/null +++ b/src/modules/utils/currentcontrol/CurrentControl.h @@ -0,0 +1,25 @@ +#ifndef CURRENTCONTROL_H +#define CURRENTCONTROL_H + +#include "Module.h" + +class DigipotBase; + +class CurrentControl : public Module { + public: + CurrentControl(); + virtual ~CurrentControl() {}; + + void on_module_loaded(); + void on_gcode_received(void *); + + private: + DigipotBase* digipot; + +}; + + + + + +#endif diff --git a/src/modules/utils/currentcontrol/DigipotBase.h b/src/modules/utils/currentcontrol/DigipotBase.h new file mode 100644 index 00000000..2d6fabf4 --- /dev/null +++ b/src/modules/utils/currentcontrol/DigipotBase.h @@ -0,0 +1,20 @@ +#ifndef DIGIPOTBASE_H +#define DIGIPOTBASE_H + +class DigipotBase { + public: + DigipotBase(){} + virtual ~DigipotBase(){} + + virtual void set_current( int channel, float current )= 0; + virtual float get_current(int channel)= 0; + void set_max_current(float c) { max_current= c; } + void set_factor(float f) { factor= f; } + + protected: + float factor; + float max_current; +}; + + +#endif diff --git a/src/modules/utils/currentcontrol/ad5206.h b/src/modules/utils/currentcontrol/ad5206.h new file mode 100644 index 00000000..6d8dc112 --- /dev/null +++ b/src/modules/utils/currentcontrol/ad5206.h @@ -0,0 +1,62 @@ +#ifndef AD5206_H +#define AD5206_H + +#include "libs/Kernel.h" +#include "libs/utils.h" +#include +#include "mbed.h" +#include +#include + +class AD5206 : public DigipotBase { + public: + AD5206(){ + //SPI0_SCK + //this->spi= new mbed::SPI(P0_9,P0_8,P0_7); //should be able to set those pins in config + this->spi= new mbed::SPI(SPI0_MOSI,SPI0_MISO,SPI0_SCK); //should be able to set those pins in config + cs.from_string("4.29")->as_output(); //this also should be configurable + cs.set(1); + for (int i = 0; i < 6; i++) currents[i] = -1; + } + + void set_current( int channel, float current ) + { + if(channel<6){ + if(current < 0) { + currents[channel]= -1; + return; + } + current = min( max( current, 0.0F ), 2.0F ); + char adresses[6] = { 0x05, 0x03, 0x01, 0x00, 0x02, 0x04 }; + currents[channel] = current; + cs.set(0); + spi->write((int)adresses[channel]); + spi->write((int)current_to_wiper(current)); + cs.set(1); + } + } + + + //taken from 4pi firmware + unsigned char current_to_wiper( float current ){ + unsigned int count = int((current*1000)*100/743); //6.8k resistor and 10k pot + + return (unsigned char)count; + } + + float get_current(int channel) + { + if(channel < 6) + return currents[channel]; + return -1; + } + + private: + + Pin cs; + mbed::SPI* spi; + float currents[6]; +}; + + +#endif diff --git a/src/modules/utils/currentcontrol/mcp4451.h b/src/modules/utils/currentcontrol/mcp4451.h new file mode 100644 index 00000000..510b9f94 --- /dev/null +++ b/src/modules/utils/currentcontrol/mcp4451.h @@ -0,0 +1,71 @@ +#ifndef MCP4451_H +#define MCP4451_H + +#include "libs/Kernel.h" +#include "I2C.h" +#include "libs/utils.h" +#include "DigipotBase.h" +#include +#include + +class MCP4451 : public DigipotBase { + public: + MCP4451(){ + // I2C com + this->i2c = new mbed::I2C(I2C0_SDA, I2C0_SCL); + this->i2c->frequency(20000); + for (int i = 0; i < 8; i++) currents[i] = -1; + } + + ~MCP4451(){ + delete this->i2c; + } + + void set_current( int channel, float current ) + { + if(current < 0) { + currents[channel]= -1; + return; + } + current = min( (float) max( current, 0.0f ), this->max_current ); + currents[channel] = current; + char addr = 0x58; + while(channel > 3){ + addr += 0x02; + channel -= 4; + } + + // Initial setup + this->i2c_send( addr, 0x40, 0xff ); + this->i2c_send( addr, 0xA0, 0xff ); + + // Set actual wiper value + char addresses[4] = { 0x00, 0x10, 0x60, 0x70 }; + this->i2c_send( addr, addresses[channel], this->current_to_wiper(current) ); + } + + float get_current(int channel) + { + return currents[channel]; + } + + private: + + void i2c_send( char first, char second, char third ){ + this->i2c->start(); + this->i2c->write(first); + this->i2c->write(second); + this->i2c->write(third); + this->i2c->stop(); + } + + char current_to_wiper( float current ){ + return char(ceilf(float((this->factor*current)))); + } + + mbed::I2C* i2c; + float currents[8]; +}; + + +#endif diff --git a/src/modules/utils/killbutton/KillButton.cpp b/src/modules/utils/killbutton/KillButton.cpp new file mode 100644 index 00000000..cdceb1e9 --- /dev/null +++ b/src/modules/utils/killbutton/KillButton.cpp @@ -0,0 +1,109 @@ +#include "libs/Kernel.h" +#include "KillButton.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "Config.h" +#include "SlowTicker.h" +#include "libs/SerialMessage.h" +#include "libs/StreamOutput.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "StreamOutputPool.h" + +using namespace std; + +#define pause_button_enable_checksum CHECKSUM("pause_button_enable") +#define kill_button_enable_checksum CHECKSUM("kill_button_enable") +#define unkill_checksum CHECKSUM("unkill_enable") +#define pause_button_pin_checksum CHECKSUM("pause_button_pin") +#define kill_button_pin_checksum CHECKSUM("kill_button_pin") + +KillButton::KillButton() +{ + this->state= IDLE; +} + +void KillButton::on_module_loaded() +{ + bool pause_enable = THEKERNEL->config->value( pause_button_enable_checksum )->by_default(false)->as_bool(); // @deprecated + bool kill_enable = pause_enable || THEKERNEL->config->value( kill_button_enable_checksum )->by_default(false)->as_bool(); + if(!kill_enable) { + delete this; + return; + } + this->unkill_enable = THEKERNEL->config->value( unkill_checksum )->by_default(true)->as_bool(); + + Pin pause_button; + pause_button.from_string( THEKERNEL->config->value( pause_button_pin_checksum )->by_default("2.12")->as_string())->as_input(); // @DEPRECATED + this->kill_button.from_string( THEKERNEL->config->value( kill_button_pin_checksum )->by_default("nc")->as_string())->as_input(); + + if(!this->kill_button.connected() && pause_button.connected()) { + // use pause button for kill button if kill button not specifically defined + this->kill_button = pause_button; + } + + if(!this->kill_button.connected()) { + delete this; + return; + } + + this->register_for_event(ON_IDLE); + THEKERNEL->slow_ticker->attach( 5, this, &KillButton::button_tick ); +} + +void KillButton::on_idle(void *argument) +{ + if(state == KILL_BUTTON_DOWN) { + if(!THEKERNEL->is_halted()) { + THEKERNEL->call_event(ON_HALT, nullptr); + THEKERNEL->streams->printf("Kill button pressed - reset or M999 to continue\r\n"); + } + + }else if(state == UNKILL_FIRE) { + if(THEKERNEL->is_halted()) { + THEKERNEL->call_event(ON_HALT, (void *)1); // clears on_halt + THEKERNEL->streams->printf("UnKill button pressed Halt cleared\r\n"); + } + } +} + +// Check the state of the button and act accordingly using the following FSM +// Note this is ISR so don't do anything nasty in here +uint32_t KillButton::button_tick(uint32_t dummy) +{ + bool killed= THEKERNEL->is_halted(); + + switch(state) { + case IDLE: + if(!this->kill_button.get()) state= KILL_BUTTON_DOWN; + else if(unkill_enable && killed) state= KILLED_BUTTON_UP; // allow kill button to unkill if kill was created fromsome other source + break; + case KILL_BUTTON_DOWN: + if(killed) state= KILLED_BUTTON_DOWN; + break; + case KILLED_BUTTON_DOWN: + if(this->kill_button.get()) state= KILLED_BUTTON_UP; + break; + case KILLED_BUTTON_UP: + if(!killed) state= IDLE; + else if(unkill_enable && !this->kill_button.get()) state= UNKILL_BUTTON_DOWN; + break; + case UNKILL_BUTTON_DOWN: + unkill_timer= 0; + state= UNKILL_TIMING_BUTTON_DOWN; + break; + case UNKILL_TIMING_BUTTON_DOWN: + if(++unkill_timer > 5*2) state= UNKILL_FIRE; + else if(this->kill_button.get()) unkill_timer= 0; + if(!killed) state= IDLE; + break; + case UNKILL_FIRE: + if(!killed) state= UNKILLED_BUTTON_DOWN; + break; + case UNKILLED_BUTTON_DOWN: + if(this->kill_button.get()) state= IDLE; + break; + } + + return 0; +} diff --git a/src/modules/utils/killbutton/KillButton.h b/src/modules/utils/killbutton/KillButton.h new file mode 100644 index 00000000..2bc2dfe9 --- /dev/null +++ b/src/modules/utils/killbutton/KillButton.h @@ -0,0 +1,31 @@ +#pragma once + +#include "libs/Pin.h" + +class KillButton : public Module { + public: + KillButton(); + + void on_module_loaded(); + void on_idle(void *argument); + uint32_t button_tick(uint32_t dummy); + + private: + Pin kill_button; + enum STATE { + IDLE, + KILL_BUTTON_DOWN, + KILLED_BUTTON_DOWN, + KILLED_BUTTON_UP, + UNKILL_BUTTON_DOWN, + UNKILL_TIMING_BUTTON_DOWN, + UNKILL_FIRE, + UNKILLED_BUTTON_DOWN + }; + + struct { + uint8_t unkill_timer:6; + volatile STATE state:4; + bool unkill_enable:1; + }; +}; diff --git a/src/modules/utils/motordrivercontrol/MotorDriverControl.cpp b/src/modules/utils/motordrivercontrol/MotorDriverControl.cpp new file mode 100644 index 00000000..4dcedf8a --- /dev/null +++ b/src/modules/utils/motordrivercontrol/MotorDriverControl.cpp @@ -0,0 +1,522 @@ +#include "MotorDriverControl.h" +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "ConfigValue.h" +#include "libs/StreamOutput.h" +#include "libs/StreamOutputPool.h" +#include "Robot.h" +#include "StepperMotor.h" +#include "PublicDataRequest.h" +#include "Pin.h" + +#include "Gcode.h" +#include "Config.h" +#include "checksumm.h" + +#include "mbed.h" // for SPI + +#include "drivers/TMC26X/TMC26X.h" +#include "drivers/DRV8711/drv8711.h" +#include "drivers/TMC21X/TMC21X.h" + +#include + +#define motor_driver_control_checksum CHECKSUM("motor_driver_control") +#define enable_checksum CHECKSUM("enable") +#define chip_checksum CHECKSUM("chip") +#define designator_checksum CHECKSUM("designator") +#define alarm_checksum CHECKSUM("alarm") +#define halt_on_alarm_checksum CHECKSUM("halt_on_alarm") + +#define current_checksum CHECKSUM("current") +#define hold_current_checksum CHECKSUM("hold_current") //TMC2130 has a hold current ration of 1/32 to 32/32 +#define max_current_checksum CHECKSUM("max_current") + +#define microsteps_checksum CHECKSUM("microsteps") +#define decay_mode_checksum CHECKSUM("decay_mode") + +#define raw_register_checksum CHECKSUM("reg") + +#define spi_channel_checksum CHECKSUM("spi_channel") +#define spi_cs_pin_checksum CHECKSUM("spi_cs_pin") +#define spi_frequency_checksum CHECKSUM("spi_frequency") + +MotorDriverControl::MotorDriverControl(uint8_t id) : id(id) +{ + enable_event= false; + current_override= false; + microstep_override= false; +} + +MotorDriverControl::~MotorDriverControl() +{ +} + +// this will load all motor driver controls defined in config, called from main +void MotorDriverControl::on_module_loaded() +{ + vector modules; + THEKERNEL->config->get_module_list( &modules, motor_driver_control_checksum ); + uint8_t cnt = 1; + for( auto cs : modules ) { + // If module is enabled create an instance and initialize it + if( THEKERNEL->config->value(motor_driver_control_checksum, cs, enable_checksum )->as_bool() ) { + MotorDriverControl *controller = new MotorDriverControl(cnt++); + if(!controller->config_module(cs)) delete controller; + } + } + + // we don't need this instance anymore + delete this; +} + +bool MotorDriverControl::config_module(uint16_t cs) +{ + std::string str= THEKERNEL->config->value( motor_driver_control_checksum, cs, designator_checksum)->by_default("")->as_string(); + if(str.empty()) { + THEKERNEL->streams->printf("MotorDriverControl ERROR: designator not defined\n"); + return false; // designator required + } + designator= str[0]; + + spi_cs_pin.from_string(THEKERNEL->config->value( motor_driver_control_checksum, cs, spi_cs_pin_checksum)->by_default("nc")->as_string())->as_output(); + if(!spi_cs_pin.connected()) { + THEKERNEL->streams->printf("MotorDriverControl %c ERROR: chip select not defined\n", designator); + return false; // if not defined then we can't use this instance + } + spi_cs_pin.set(1); + + + str= THEKERNEL->config->value( motor_driver_control_checksum, cs, chip_checksum)->by_default("")->as_string(); + if(str.empty()) { + THEKERNEL->streams->printf("MotorDriverControl %c ERROR: chip type not defined\n", designator); + return false; // chip type required + } + + using std::placeholders::_1; + using std::placeholders::_2; + using std::placeholders::_3; + + if(str == "DRV8711") { + chip= DRV8711; + drv8711= new DRV8711DRV(std::bind( &MotorDriverControl::sendSPI, this, _1, _2, _3), designator); + + }else if(str == "TMC2130") { + chip= TMC2130; + tmc21x= new TMC21X(std::bind( &MotorDriverControl::sendSPI, this, _1, _2, _3), designator); + }else if(str == "TMC2660") { + chip= TMC2660; + tmc26x= new TMC26X(std::bind( &MotorDriverControl::sendSPI, this, _1, _2, _3), designator); + + }else{ + THEKERNEL->streams->printf("MotorDriverControl %c ERROR: Unknown chip type: %s\n", designator, str.c_str()); + return false; + } + + // select which SPI channel to use + int spi_channel = THEKERNEL->config->value(motor_driver_control_checksum, cs, spi_channel_checksum)->by_default(1)->as_number(); + int spi_frequency = THEKERNEL->config->value(motor_driver_control_checksum, cs, spi_frequency_checksum)->by_default(1000000)->as_number(); + + // select SPI channel to use + PinName mosi, miso, sclk; + if(spi_channel == 0) { + mosi = SPI0_MOSI; miso = SPI0_MISO; sclk = SPI0_SCK; + } else if(spi_channel == 1) { + mosi = SPI1_MOSI; miso = SPI1_MISO; sclk = SPI1_SCK; + } else { + THEKERNEL->streams->printf("MotorDriverControl %c ERROR: Unknown SPI Channel: %d\n", designator, spi_channel); + return false; + } + + this->spi = new mbed::SPI(mosi, miso, sclk); + this->spi->frequency(spi_frequency); + this->spi->format(8, 3); // 8bit, mode3 + + // set default max currents for each chip, can be overridden in config + switch(chip) { + case DRV8711: max_current= DRV8711_max_current; break; + case TMC2660: max_current= TMC2660_max_current; break; + case TMC2130: max_current= TMC2130_max_current; break; //TMC2130 only allows a ratio of 0..31 which represents 1/32 .. 32/32 + } + + max_current= THEKERNEL->config->value(motor_driver_control_checksum, cs, max_current_checksum )->by_default((int)max_current)->as_number(); + //current_factor= THEKERNEL->config->value(motor_driver_control_checksum, cs, current_factor_checksum )->by_default(1.0F)->as_number(); + + current= THEKERNEL->config->value(motor_driver_control_checksum, cs, current_checksum )->by_default((int)max_current)->as_number(); // set to maxCurrent as TMC2130 uses a ratio method and not mA + hold_current= THEKERNEL->config->value(motor_driver_control_checksum, cs, hold_current_checksum )->by_default((int)max_current)->as_number(); // set to maxCurrent as TMC2130 uses a ratio method and not mA + microsteps= THEKERNEL->config->value(motor_driver_control_checksum, cs, microsteps_checksum )->by_default(16)->as_number(); // 1/n + //decay_mode= THEKERNEL->config->value(motor_driver_control_checksum, cs, decay_mode_checksum )->by_default(1)->as_number(); + + // setup the chip via SPI + initialize_chip(cs); + + // if raw registers are defined set them 1,2,3 etc in hex + str= THEKERNEL->config->value( motor_driver_control_checksum, cs, raw_register_checksum)->by_default("")->as_string(); + if(!str.empty()) { + rawreg= true; + std::vector regs= parse_number_list(str.c_str(), 16); + if(!regs.empty()) { + uint32_t reg= 0; + for(auto i : regs) { + // this just sets the local storage, it does not write to the chip + switch(chip) { + case DRV8711: drv8711->set_raw_register(&StreamOutput::NullStream, ++reg, i); break; + case TMC2660: tmc26x->setRawRegister(&StreamOutput::NullStream, ++reg, i); break; + case TMC2130: tmc21x->setRawRegister(&StreamOutput::NullStream, ++reg, i); break; + } + } + + // write the stored registers + switch(chip) { + case DRV8711: drv8711->set_raw_register(&StreamOutput::NullStream, 255, 0); break; + case TMC2660: tmc26x->setRawRegister(&StreamOutput::NullStream, 255, 0); break; + case TMC2130: tmc21x->setRawRegister(&StreamOutput::NullStream, 255, 0); break; + } + } + + }else{ + rawreg= false; + } + + this->register_for_event(ON_GCODE_RECEIVED); + this->register_for_event(ON_HALT); + this->register_for_event(ON_ENABLE); + this->register_for_event(ON_IDLE); + + if( THEKERNEL->config->value(motor_driver_control_checksum, cs, alarm_checksum )->by_default(false)->as_bool() ) { + halt_on_alarm= THEKERNEL->config->value(motor_driver_control_checksum, cs, halt_on_alarm_checksum )->by_default(false)->as_bool(); + // enable alarm monitoring for the chip + this->register_for_event(ON_SECOND_TICK); + } +//TODO fix below to actuall print out the human readable MBED pin name, e.e P3_0 rather than its address + THEKERNEL->streams->printf("MotorDriverControl INFO: configured motor %c (%d): as %s, cs: %04X\n", designator, id, chip==TMC2660?"TMC2660":chip==DRV8711?"DRV8711":chip==TMC2130?"TMC2130":"UNKNOWN", spi_cs_pin.getPinName()); + + return true; +} + +// event to handle enable on/off, as it could be called in an ISR we schedule to turn the steppers on or off in ON_IDLE +// This may cause the initial step to be missed if on-idle is delayed too much but we can't do SPI in an interrupt +void MotorDriverControl::on_enable(void *argument) +{ + enable_event= true; + enable_flg= (argument != nullptr); +} + +void MotorDriverControl::on_idle(void *argument) +{ + if(enable_event) { + enable_event= false; + enable(enable_flg); + } +} + +void MotorDriverControl::on_halt(void *argument) +{ + if(argument == nullptr) { + enable(false); + } +} + +// runs in on_idle, does SPI transaction +void MotorDriverControl::on_second_tick(void *argument) +{ + // we don't want to keep checking once we have been halted by an error + if(THEKERNEL->is_halted()) return; + + bool alarm=false;; + switch(chip) { + case DRV8711: + alarm= drv8711->check_alarm(); + break; + + case TMC2660: + alarm= tmc26x->checkAlarm(); + break; + + case TMC2130: + alarm= tmc21x->checkAlarm(); + break; + } + + if(halt_on_alarm && alarm) { + THEKERNEL->call_event(ON_HALT, nullptr); + THEKERNEL->streams->printf("Motor Driver alarm - reset or M999 required to continue\r\n"); + } +} + +void MotorDriverControl::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + + if (gcode->has_m) { + if(gcode->m == 906) { + if (gcode->has_letter(designator)) { + // set motor currents in mA (Note not using M907 as digipots use that) + current= gcode->get_value(designator); + current= std::min(current, max_current); + set_current(current); + current_override= true; + } + + } else if(gcode->m == 909) { // M909 Annn set microstepping, M909.1 also change steps/mm + if (gcode->has_letter(designator)) { + uint32_t current_microsteps= microsteps; + microsteps= gcode->get_value(designator); + microsteps= set_microstep(microsteps); // driver may change the steps it sets to + if(gcode->subcode == 1 && current_microsteps != microsteps) { + // also reset the steps/mm + int a= designator-'A'; + if(a >= 0 && a <=2) { + float s= THEKERNEL->robot->actuators[a]->get_steps_per_mm()*((float)microsteps/current_microsteps); + THEKERNEL->robot->actuators[a]->change_steps_per_mm(s); + gcode->stream->printf("steps/mm for %c changed to: %f\n", designator, s); + THEKERNEL->robot->check_max_actuator_speeds(); + } + } + microstep_override= true; + } + + // } else if(gcode->m == 910) { // set decay mode + // if (gcode->has_letter(designator)) { + // decay_mode= gcode->get_value(designator); + // set_decay_mode(decay_mode); + // } + + } else if(gcode->m == 911) { + // set or get raw registers + // M911 will dump all the registers and status of all the motors + // M911.1 Pn (or A0) will dump the registers and status of the selected motor. X0 will request format in processing machine readable format + // M911.2 Pn (or B0) Rxxx Vyyy sets Register xxx to value yyy for motor nnn, xxx == 255 writes the registers, xxx == 0 shows what registers are mapped to what + // M911.3 Pn (or C0) will set the options based on the parameters passed as below... + // TMC2660:- + // M911.3 Onnn Qnnn setStallGuardThreshold O=stall_guard_threshold, Q=stall_guard_filter_enabled + // M911.3 Hnnn Innn Jnnn Knnn Lnnn setCoolStepConfiguration H=lower_SG_threshold, I=SG_hysteresis, J=current_decrement_step_size, K=current_increment_step_size, L=lower_current_limit + // M911.3 S0 Unnn Vnnn Wnnn Xnnn Ynnn setConstantOffTimeChopper U=constant_off_time, V=blank_time, W=fast_decay_time_setting, X=sine_wave_offset, Y=use_current_comparator + // M911.3 S1 Unnn Vnnn Wnnn Xnnn Ynnn setSpreadCycleChopper U=constant_off_time, V=blank_time, W=hysteresis_start, X=hysteresis_end, Y=hysteresis_decrement + // M911.3 S2 Zn setRandomOffTime Z=on|off Z1 is on Z0 is off + // M911.3 S3 Zn setDoubleEdge Z=on|off Z1 is on Z0 is off + // M911.3 S4 Zn setStepInterpolation Z=on|off Z1 is on Z0 is off + // M911.3 S5 Zn setCoolStepEnabled Z=on|off Z1 is on Z0 is off + // TMC2130:- + // M911.3 Onnn Qnnn setStallGuardThreshold O=stall_guard_threshold, Q=stall_guard_filter_enabled + // M911.3 Hnnn Innn Jnnn Knnn Lnnn setCoolStepConfiguration H=lower_SG_threshold, I=SG_hysteresis, J=current_decrement_step_size, K=current_increment_step_size, L=lower_current_limit + // M911.3 S0 Unnn Vnnn Wnnn Xnnn Ynnn setConstantOffTimeChopper U=constant_off_time, V=blank_time, W=fast_decay_time_setting, X=sine_wave_offset, Y=use_current_comparator + // M911.3 S1 Unnn Vnnn Wnnn Xnnn Ynnn setSpreadCycleChopper U=constant_off_time, V=blank_time, W=hysteresis_start, X=hysteresis_end, Y=hysteresis_decrement + // M911.3 S2 Zn setRandomOffTime Z=on|off Z1 is on Z0 is off + // M911.3 S3 Zn setDoubleEdge Z=on|off Z1 is on Z0 is off + // M911.3 S4 Zn setStepInterpolation Z=on|off Z1 is on Z0 is off + // M911.3 S5 Zn setCoolStepEnabled Z=on|off Z1 is on Z0 is off + + if(gcode->subcode == 0 && gcode->get_num_args() == 0) { + // M911 no args dump status for all drivers, M911.1 P0|A0 dump for specific driver + gcode->stream->printf("Motor %d (%c)...\n", id, designator); + dump_status(gcode->stream, true); + + }else if(gcode->get_value('P') == id || gcode->has_letter(designator)) { + if(gcode->subcode == 1) { + dump_status(gcode->stream, !gcode->has_letter('X')); + + }else if(gcode->subcode == 2 && gcode->has_letter('R') && gcode->has_letter('V')) { + set_raw_register(gcode->stream, gcode->get_value('R'), gcode->get_value('V')); + + }else if(gcode->subcode == 3 ) { + set_options(gcode); + } + } + + } else if(gcode->m == 500 || gcode->m == 503) { + if(current_override) { + gcode->stream->printf(";Motor %c id %d current mA:\n", designator, id); + gcode->stream->printf("M906 %c%lu\n", designator, current); + } + if(microstep_override) { + gcode->stream->printf(";Motor %c id %d microsteps:\n", designator, id); + gcode->stream->printf("M909 %c%lu\n", designator, microsteps); + } + //gcode->stream->printf("M910 %c%d\n", designator, decay_mode); + } + } +} + +void MotorDriverControl::initialize_chip(uint16_t cs) +{ + // send initialization sequence to chips + if(chip == DRV8711) { + drv8711->init(cs); + set_current(current); + set_microstep(microsteps); + + }else if(chip == TMC2660){ + tmc26x->init(cs); + set_current(current); + set_microstep(microsteps); + //set_decay_mode(decay_mode); + + }else if(chip == TMC2130){ + tmc21x->init(cs); + set_current(current); + set_microstep(microsteps); + //set_decay_mode(decay_mode); + } + +} + +// set current in milliamps +void MotorDriverControl::set_current(uint32_t c) +{ + switch(chip) { + case DRV8711: + drv8711->set_current(c); + break; + + case TMC2660: + tmc26x->setCurrent(c); + break; + + case TMC2130: + tmc21x->setCurrent(c); + break; + } +} + +// set microsteps where n is the number of microsteps eg 64 for 1/64 +uint32_t MotorDriverControl::set_microstep( uint32_t n ) +{ + uint32_t m= n; + switch(chip) { + case DRV8711: + m= drv8711->set_microsteps(n); + break; + + case TMC2660: + tmc26x->setMicrosteps(n); + m= tmc26x->getMicrosteps(); + break; + + case TMC2130: + tmc21x->setMicrosteps(n); + m= tmc21x->getMicrosteps(); + break; + } + return m; +} + +// TODO how to handle this? SO many options +void MotorDriverControl::set_decay_mode( uint8_t dm ) +{ + switch(chip) { + case DRV8711: break; + case TMC2660: break; + case TMC2130: break; + } +} + +void MotorDriverControl::enable(bool on) +{ + switch(chip) { + case DRV8711: + drv8711->set_enable(on); + break; + + case TMC2660: + tmc26x->setEnabled(on); + break; + + case TMC2130: + tmc21x->setEnabled(on); + break; + } +} + +void MotorDriverControl::dump_status(StreamOutput *stream, bool b) +{ + switch(chip) { + case DRV8711: + drv8711->dump_status(stream); + break; + + case TMC2660: + tmc26x->dumpStatus(stream, b); + break; + + case TMC2130: + tmc21x->dumpStatus(stream, b); + break; + } +} + +void MotorDriverControl::set_raw_register(StreamOutput *stream, uint32_t reg, uint32_t val) +{ + bool ok= false; + switch(chip) { + case DRV8711: ok= drv8711->set_raw_register(stream, reg, val); break; + case TMC2660: ok= tmc26x->setRawRegister(stream, reg, val); break; + case TMC2130: ok= tmc21x->setRawRegister(stream, reg, val); break; + } + if(ok) { + stream->printf("register operation succeeded\n"); + }else{ + stream->printf("register operation failed\n"); + } +} + +void MotorDriverControl::set_options(Gcode *gcode) +{ + switch(chip) { + case DRV8711: break; + + case TMC2660: { + TMC26X::options_t options= gcode->get_args_int(); + if(options.size() > 0) { + if(tmc26x->set_options(options)) { + gcode->stream->printf("options set\n"); + }else{ + gcode->stream->printf("failed to set any options\n"); + } + } + // options.clear(); + // if(tmc26x->get_optional(options)) { + // // foreach optional value + // for(auto &i : options) { + // // print all current values of supported options + // gcode->stream->printf("%c: %d ", i.first, i.second); + // gcode->add_nl = true; + // } + // } + } + break; + case TMC2130: { + TMC21X::options_t options= gcode->get_args_int(); + if(options.size() > 0) { + if(tmc21x->set_options(options)) { + gcode->stream->printf("options set\n"); + }else{ + gcode->stream->printf("failed to set any options\n"); + } + } + // options.clear(); + // if(tmc21x->get_optional(options)) { + // // foreach optional value + // for(auto &i : options) { + // // print all current values of supported options + // gcode->stream->printf("%c: %d ", i.first, i.second); + // gcode->add_nl = true; + // } + // } + } + break; + } +} + +// Called by the drivers codes to send and receive SPI data to/from the chip +int MotorDriverControl::sendSPI(uint8_t *b, int cnt, uint8_t *r) +{ + spi_cs_pin.set(0); + for (int i = 0; i < cnt; ++i) { + r[i]= spi->write(b[i]); + } + spi_cs_pin.set(1); + return cnt; +} + diff --git a/src/modules/utils/motordrivercontrol/MotorDriverControl.h b/src/modules/utils/motordrivercontrol/MotorDriverControl.h new file mode 100644 index 00000000..1e2a2740 --- /dev/null +++ b/src/modules/utils/motordrivercontrol/MotorDriverControl.h @@ -0,0 +1,79 @@ +#pragma once + +#include "Module.h" +#include "Pin.h" + +#include + +namespace mbed { + class SPI; +} + +class DRV8711DRV; +class TMC26X; +class TMC21X; +class StreamOutput; +class Gcode; + +class MotorDriverControl : public Module { + public: + MotorDriverControl(uint8_t id); + virtual ~MotorDriverControl(); + + void on_module_loaded(); + void on_gcode_received(void *); + void on_halt(void *argument); + void on_enable(void *argument); + void on_idle(void *argument); + void on_second_tick(void *argument); + + private: + bool config_module(uint16_t cs); + void initialize_chip(uint16_t cs); + void set_current( uint32_t current ); + uint32_t set_microstep( uint32_t ms ); + void set_decay_mode( uint8_t dm ); + void dump_status(StreamOutput*, bool); + void set_raw_register(StreamOutput *stream, uint32_t reg, uint32_t val); + void set_options(Gcode *gcode); + + void enable(bool on); + int sendSPI(uint8_t *b, int cnt, uint8_t *r); + + Pin spi_cs_pin; + mbed::SPI *spi; + + enum CHIP_TYPE { + DRV8711, + TMC2660, + TMC2130 + }; + CHIP_TYPE chip; + + // one of these drivers + union { + DRV8711DRV *drv8711; + TMC26X *tmc26x; + TMC21X *tmc21x; + }; + + //float current_factor; + uint32_t max_current; //Normally in milliamps, but for TMC2130, 0..31 represents a ratio of 1/32 to 32/32 + uint32_t current; //Normally in milliamps, but for TMC2130, 0..31 represents a ratio of 1/32 to 32/32 + uint32_t hold_current; //For TMC2130, 0..31 represents a ratio of 1/32 to 32/32 + uint32_t microsteps; + + char designator; + + struct{ + uint8_t id:4; + uint8_t decay_mode:4; + bool rawreg:1; + bool enable_event:1; + bool enable_flg:1; + bool current_override:1; + bool microstep_override:1; + bool halt_on_alarm:1; + }; + +}; diff --git a/src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.cpp b/src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.cpp new file mode 100644 index 00000000..0c048f26 --- /dev/null +++ b/src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.cpp @@ -0,0 +1,459 @@ +#include "drv8711.h" + +#include "Kernel.h" +#include "StreamOutput.h" +#include "StreamOutputPool.h" +#include "ConfigValue.h" +#include "Config.h" +#include "checksumm.h" + +#define motor_driver_control_checksum CHECKSUM("motor_driver_control") +#define gain_checksum CHECKSUM("gain") +#define sense_resistor_checksum CHECKSUM("sense_resistor") + + +#define REGWRITE 0x00 +#define REGREAD 0x80 + +DRV8711DRV::DRV8711DRV(std::function spi, char d) : spi(spi), designator(d) +{ + error_reported.reset(); +} + +void DRV8711DRV::init (uint16_t cs) +{ + // read chip specific config entries + this->gain= THEKERNEL->config->value(motor_driver_control_checksum, cs, gain_checksum)->by_default(20)->as_number(); + this->resistor= THEKERNEL->config->value(motor_driver_control_checksum, cs, sense_resistor_checksum)->by_default(0.05F)->as_number(); // in ohms + + // initialize the in memory mirror of the registers + + // CTRL Register + G_CTRL_REG.raw = 0x0000; + G_CTRL_REG.Address = 0x00; + G_CTRL_REG.DTIME = 0x03; //850ns + G_CTRL_REG.EXSTALL = 0x00; //Internal Stall Detect + G_CTRL_REG.ISGAIN = 0x02; //Gain of 20 + G_CTRL_REG.MODE = 0x04; // also set by set_microstep() + G_CTRL_REG.RSTEP = 0x00; //No Action + G_CTRL_REG.RDIR = 0x00; //Direction set by DIR Pin + G_CTRL_REG.ENBL = 0x00; //enable motor, start disabled + + /// TORQUE Register + G_TORQUE_REG.raw = 0x0000; + G_TORQUE_REG.Address = 0x01; + G_TORQUE_REG.SIMPLTH = 0x01; //100uS Back EMF Sample Threshold + G_TORQUE_REG.TORQUE = 0x01; // low default set by set_current() + + // OFF Register + G_OFF_REG.raw = 0x0000; + G_OFF_REG.Address = 0x02; + G_OFF_REG.PWMMODE = 0x00; //Internal Indexer + G_OFF_REG.TOFF = 0x32; //Default + + // BLANK Register + G_BLANK_REG.raw = 0x0000; + G_BLANK_REG.Address = 0x03; + G_BLANK_REG.ABT = 0x01; //enable adaptive blanking time + G_BLANK_REG.TBLANK = 0x00; //no idea what this should be but the + + // DECAY Register. + G_DECAY_REG.raw = 0x0000; + G_DECAY_REG.Address = 0x04; + G_DECAY_REG.DECMOD = 0x05; // auto mixed decay + G_DECAY_REG.TDECAY = 0x10; //default + + // STALL Register + G_STALL_REG.raw = 0x0000; + G_STALL_REG.Address = 0x05; + G_STALL_REG.VDIV = 0x02; //Back EMF is divided by 8 + G_STALL_REG.SDCNT = 0x01; //stalln asserted after 2 steps + G_STALL_REG.SDTHR = 0x02; //recommended + + // DRIVE Register + G_DRIVE_REG.raw = 0x0000; + G_DRIVE_REG.Address = 0x06; + G_DRIVE_REG.IDRIVEP = 0x00; //High Side 50mA peak (source) + G_DRIVE_REG.IDRIVEN = 0x00; //Low Side 100mA peak (sink) + G_DRIVE_REG.TDRIVEP = 0x00; //High Side gate drive 500nS + G_DRIVE_REG.TDRIVEN = 0x00; //Low Side Gate Drive 500nS + G_DRIVE_REG.OCPDEG = 0x00; //OCP Deglitch Time 2uS + G_DRIVE_REG.OCPTH = 0x00; //OCP Threshold 500mV + + // STATUS Register + G_STATUS_REG.raw = 0x0000; + G_STATUS_REG.Address = 0x07; + G_STATUS_REG.STDLAT = 0x00; + G_STATUS_REG.STD = 0x00; + G_STATUS_REG.UVLO = 0x00; + G_STATUS_REG.BPDF = 0x00; + G_STATUS_REG.APDF = 0x00; + G_STATUS_REG.BOCP = 0x00; + G_STATUS_REG.AOCP = 0x00; + G_STATUS_REG.OTS = 0x00; + + WriteAllRegisters(); +} + +void DRV8711DRV::set_current(uint32_t currentma) +{ + // derive torque and gain from current + float c = currentma / 1000.0F; // current in amps + // I = (2.75 * Torque) / (256 * GAIN * Rsense) use (5,10,20,40) for gain, 0.05 is RSense Torque is 0-255 + // torque= (256*gain*resistor*I)/2.75 + float a = 256.0F * gain * resistor; + float t = (c * a) / 2.75F; + while(t > 255) { + // reduce gain + gain = gain / 2; + if(gain < 5) { + gain = 5; + t = 255; + break; + } + a = 256.0F * gain * resistor; + t = (c * a) / 2.75F; + } + while(t < 1.0F) { + // increase gain + gain = gain * 2; + if(gain > 40) { + gain = 40; + t = 1; + break; + } + a = 256.0F * gain * resistor; + t = (c * a) / 2.75F; + } + + G_TORQUE_REG.TORQUE = t; + + switch (gain) { + case 5: + G_CTRL_REG.ISGAIN = 0x0; //Gain of 5 + break; + case 10: + G_CTRL_REG.ISGAIN = 0x01; //Gain of 10 + break; + case 20: + G_CTRL_REG.ISGAIN = 0x02; //Gain of 20 + break; + case 40: + G_CTRL_REG.ISGAIN = 0x03; //Gain of 40 + break; + } + + //THEKERNEL->streams->printf("for requested current of %lumA, torque= %u, gain= %u, actual current= %fA\n", currentma, G_TORQUE_REG.TORQUE, gain, (2.75F * t) / (256.0F * gain * resistor)); + // for current of 1.500000A, torque= 139, gain= 20 + + // set GAIN + uint8_t dataHi = REGWRITE | ((G_CTRL_REG.raw >> 8) & 0x7F); + uint8_t dataLo = (G_CTRL_REG.raw & 0x00FF); + ReadWriteRegister(dataHi, dataLo); + + // set TORQUE + dataHi = REGWRITE | ((G_TORQUE_REG.raw >> 8) & 0x7F); + dataLo = (G_TORQUE_REG.raw & 0x00FF); + ReadWriteRegister(dataHi, dataLo); +} + +int DRV8711DRV::set_microsteps(int number_of_steps) +{ + int microsteps; + if (number_of_steps >= 256) { + G_CTRL_REG.MODE = 0x08; + microsteps = 256; + } else if (number_of_steps >= 128) { + G_CTRL_REG.MODE = 0x07; + microsteps = 128; + } else if (number_of_steps >= 64) { + G_CTRL_REG.MODE = 0x06; + microsteps = 64; + } else if (number_of_steps >= 32) { + G_CTRL_REG.MODE = 0x05; + microsteps = 32; + } else if (number_of_steps >= 16) { + G_CTRL_REG.MODE = 0x04; + microsteps = 16; + } else if (number_of_steps >= 8) { + G_CTRL_REG.MODE = 0x03; + microsteps = 8; + } else if (number_of_steps >= 4) { + G_CTRL_REG.MODE = 0x02; + microsteps = 4; + } else if (number_of_steps >= 2) { + G_CTRL_REG.MODE = 0x01; + microsteps = 2; + } else { + //1 and 0 lead to full step + G_CTRL_REG.MODE = 0x0; + microsteps = 1; + } + + uint8_t dataHi = REGWRITE | ((G_CTRL_REG.raw >> 8) & 0x7F); + uint8_t dataLo = (G_CTRL_REG.raw & 0x00FF); + ReadWriteRegister(dataHi, dataLo); + return microsteps; +} + +void DRV8711DRV::set_enable (bool enable) +{ + // Set Enable + G_CTRL_REG.ENBL = enable ? 0x01 : 0x00; + uint8_t dataHi = REGWRITE | ((G_CTRL_REG.raw >> 8) & 0x7F); + uint8_t dataLo = (G_CTRL_REG.raw & 0x00FF); + ReadWriteRegister(dataHi, dataLo); +} + +void DRV8711DRV::dump_status(StreamOutput *stream) +{ + CTRL_Register_t R_CTRL_REG; + TORQUE_Register_t R_TORQUE_REG; + OFF_Register_t R_OFF_REG; + BLANK_Register_t R_BLANK_REG; + DECAY_Register_t R_DECAY_REG; + STALL_Register_t R_STALL_REG; + DRIVE_Register_t R_DRIVE_REG; + STATUS_Register_t R_STATUS_REG; + + stream->printf("designator: %c, Register Dump:\n", designator); + + // Read CTRL Register + R_CTRL_REG.raw= ReadRegister(G_CTRL_REG.Address); + stream->printf("CTRL: %04X (%04X): ", R_CTRL_REG.raw & 0x0FFF, G_CTRL_REG.raw & 0x0FFF); + stream->printf("DTIME: %u, ISGAIN: %u, EXSTALL: %u, MODE: %u, RSTEP: %u, RDIR: %u, ENBL: %u - ", + R_CTRL_REG.DTIME, R_CTRL_REG.ISGAIN, R_CTRL_REG.EXSTALL, R_CTRL_REG.MODE, R_CTRL_REG.RSTEP, R_CTRL_REG.RDIR, R_CTRL_REG.ENBL); + stream->printf("(DTIME: %u, ISGAIN: %u, EXSTALL: %u, MODE: %u, RSTEP: %u, RDIR: %u, ENBL: %u)\n", + G_CTRL_REG.DTIME, G_CTRL_REG.ISGAIN, G_CTRL_REG.EXSTALL, G_CTRL_REG.MODE, G_CTRL_REG.RSTEP, G_CTRL_REG.RDIR, G_CTRL_REG.ENBL); + + // Read TORQUE Register + R_TORQUE_REG.raw= ReadRegister(G_TORQUE_REG.Address); + stream->printf("TORQUE: %04X (%04X):", R_TORQUE_REG.raw & 0x0FFF, G_TORQUE_REG.raw & 0x0FFF); + stream->printf("SIMPLTH: %u, TORQUE: %u - ", R_TORQUE_REG.SIMPLTH, R_TORQUE_REG.TORQUE); + stream->printf("(SIMPLTH: %u, TORQUE: %u)\n", G_TORQUE_REG.SIMPLTH, G_TORQUE_REG.TORQUE); + + // Read OFF Register + R_OFF_REG.raw= ReadRegister(G_OFF_REG.Address); + stream->printf("OFF: %04X (%04X) - ", R_OFF_REG.raw & 0x0FFF, G_OFF_REG.raw & 0x0FFF); + stream->printf("PWMMODE: %u, TOFF: %u - ", R_OFF_REG.PWMMODE, R_OFF_REG.TOFF); + stream->printf("(PWMMODE: %u, TOFF: %u)\n", G_OFF_REG.PWMMODE, G_OFF_REG.TOFF); + + // Read BLANK Register + R_BLANK_REG.raw= ReadRegister(G_BLANK_REG.Address); + stream->printf("BLANK: %04X (%04X) - ", R_BLANK_REG.raw & 0x0FFF, G_BLANK_REG.raw & 0x0FFF); + stream->printf("ABT: %u, TBLANK: %u - ", R_BLANK_REG.ABT, R_BLANK_REG.TBLANK); + stream->printf("(ABT: %u, TBLANK: %u)\n", G_BLANK_REG.ABT, G_BLANK_REG.TBLANK); + + // Read DECAY Register + R_DECAY_REG.raw= ReadRegister(G_DECAY_REG.Address); + stream->printf("DECAY: %04X (%04X) - ", R_DECAY_REG.raw & 0x0FFF, G_DECAY_REG.raw & 0x0FFF); + stream->printf("DECMOD: %u, TDECAY: %u - ", R_DECAY_REG.DECMOD, R_DECAY_REG.TDECAY); + stream->printf("(DECMOD: %u, TDECAY: %u)\n", G_DECAY_REG.DECMOD, G_DECAY_REG.TDECAY); + + // Read STALL Register + R_STALL_REG.raw= ReadRegister(G_STALL_REG.Address); + stream->printf("STALL: %04X (%04X) - ", R_STALL_REG.raw & 0x0FFF, G_STALL_REG.raw & 0x0FFF); + stream->printf("VDIV: %u, SDCNT: %u, SDTHR: %u - ", R_STALL_REG.VDIV, R_STALL_REG.SDCNT, R_STALL_REG.SDTHR); + stream->printf("(VDIV: %u, SDCNT: %u, SDTHR: %u)\n", G_STALL_REG.VDIV, G_STALL_REG.SDCNT, G_STALL_REG.SDTHR); + + // Read DRIVE Register + R_DRIVE_REG.raw= ReadRegister(G_DRIVE_REG.Address); + stream->printf("DRIVE: %04X (%04X) - ", R_DRIVE_REG.raw & 0x0FFF, G_DRIVE_REG.raw & 0x0FFF); + stream->printf("IDRIVEP: %u, IDRIVEN: %u, TDRIVEP: %u, TDRIVEN: %u, OCPDEG: %u, OCPTH: %u - ", + R_DRIVE_REG.IDRIVEP, R_DRIVE_REG.IDRIVEN, R_DRIVE_REG.TDRIVEP, R_DRIVE_REG.TDRIVEN, R_DRIVE_REG.OCPDEG, R_DRIVE_REG.OCPTH); + stream->printf("(IDRIVEP: %u, IDRIVEN: %u, TDRIVEP: %u, TDRIVEN: %u, OCPDEG: %u, OCPTH: %u)\n", + G_DRIVE_REG.IDRIVEP, G_DRIVE_REG.IDRIVEN, G_DRIVE_REG.TDRIVEP, G_DRIVE_REG.TDRIVEN, G_DRIVE_REG.OCPDEG, G_DRIVE_REG.OCPTH); + + // Read STATUS Register + R_STATUS_REG.raw= ReadRegister(G_STATUS_REG.Address); + stream->printf("STATUS: %02X - ", R_STATUS_REG.raw & 0x00FF); + stream->printf("STDLAT: %u, STD: %u, UVLO: %u, BPDF: %u, APDF: %u, BOCP: %u, AOCP: %u, OTS: %u\n", + R_STATUS_REG.STDLAT, R_STATUS_REG.STD, R_STATUS_REG.UVLO, R_STATUS_REG.BPDF, R_STATUS_REG.APDF, R_STATUS_REG.BOCP, R_STATUS_REG.AOCP, R_STATUS_REG.OTS); + + int gain = R_CTRL_REG.ISGAIN == 0 ? 5 : R_CTRL_REG.ISGAIN == 1 ? 10 : R_CTRL_REG.ISGAIN == 2 ? 20 : R_CTRL_REG.ISGAIN == 3 ? 40 : 0; + stream->printf(" Current: %f\n", (2.75F * R_TORQUE_REG.TORQUE) / (256.0F * gain * resistor)); + stream->printf(" Microsteps: 1/%d\n", R_CTRL_REG.MODE > 0 ? 2 << (R_CTRL_REG.MODE-1) : 1); + + stream->printf(" motor_driver_control.xxx.reg %03X,%03X,%03X,%03X,%03X,%03X,%03X\n", + G_CTRL_REG.raw & 0x0FFF, G_TORQUE_REG.raw & 0x0FFF, G_OFF_REG.raw & 0x0FFF, G_BLANK_REG.raw & 0x0FFF, G_DECAY_REG.raw & 0x0FFF, G_STALL_REG.raw & 0x0FFF, G_DRIVE_REG.raw & 0x0FFF); +} + +void DRV8711DRV::WriteAllRegisters() +{ + uint8_t dataHi = 0x00; + uint8_t dataLo = 0x00; + + // Write CTRL Register + dataHi = REGWRITE | (G_CTRL_REG.Address << 4) | (G_CTRL_REG.DTIME << 2) | (G_CTRL_REG.ISGAIN); + dataLo = (G_CTRL_REG.EXSTALL << 7) | (G_CTRL_REG.MODE << 3) | (G_CTRL_REG.RSTEP << 2) | (G_CTRL_REG.RDIR << 1) | (G_CTRL_REG.ENBL); + ReadWriteRegister(dataHi, dataLo); + + // Write TORQUE Register + dataHi = REGWRITE | (G_TORQUE_REG.Address << 4) | (G_TORQUE_REG.SIMPLTH); + dataLo = G_TORQUE_REG.TORQUE; + ReadWriteRegister(dataHi, dataLo); + + // Write OFF Register + dataHi = REGWRITE | (G_OFF_REG.Address << 4) | (G_OFF_REG.PWMMODE); + dataLo = G_OFF_REG.TOFF; + ReadWriteRegister(dataHi, dataLo); + + // Write BLANK Register + dataHi = REGWRITE | (G_BLANK_REG.Address << 4) | (G_BLANK_REG.ABT); + dataLo = G_BLANK_REG.TBLANK; + ReadWriteRegister(dataHi, dataLo); + + // Write DECAY Register + dataHi = REGWRITE | (G_DECAY_REG.Address << 4) | (G_DECAY_REG.DECMOD); + dataLo = G_DECAY_REG.TDECAY; + ReadWriteRegister(dataHi, dataLo); + + // Write STALL Register + dataHi = REGWRITE | (G_STALL_REG.Address << 4) | (G_STALL_REG.VDIV << 2) | (G_STALL_REG.SDCNT); + dataLo = G_STALL_REG.SDTHR; + ReadWriteRegister(dataHi, dataLo); + + // Write DRIVE Register + dataHi = REGWRITE | (G_DRIVE_REG.Address << 4) | (G_DRIVE_REG.IDRIVEP << 2) | (G_DRIVE_REG.IDRIVEN); + dataLo = (G_DRIVE_REG.TDRIVEP << 6) | (G_DRIVE_REG.TDRIVEN << 4) | (G_DRIVE_REG.OCPDEG << 2) | (G_DRIVE_REG.OCPTH); + ReadWriteRegister(dataHi, dataLo); + + // Write STATUS Register + dataHi = REGWRITE | (G_STATUS_REG.Address << 4); + dataLo = (G_STATUS_REG.STDLAT << 7) | (G_STATUS_REG.STD << 6) | (G_STATUS_REG.UVLO << 5) | (G_STATUS_REG.BPDF << 4) | (G_STATUS_REG.APDF << 3) | (G_STATUS_REG.BOCP << 2) | (G_STATUS_REG.AOCP << 1) | (G_STATUS_REG.OTS); + ReadWriteRegister(dataHi, dataLo); +} + +bool DRV8711DRV::check_alarm() +{ + bool error= false; + STATUS_Register_t R_STATUS_REG; + // Read STATUS Register + R_STATUS_REG.raw= ReadRegister(G_STATUS_REG.Address); + + if(R_STATUS_REG.OTS) { + if(!error_reported.test(0)) THEKERNEL->streams->printf("%c, ERROR: Overtemperature shutdown\n", designator); + error= true; + error_reported.set(0); + }else{ + error_reported.reset(0); + } + + + if(R_STATUS_REG.AOCP) { + if(!error_reported.test(1)) THEKERNEL->streams->printf("%c, ERROR: Channel A over current shutdown\n", designator); + error= true; + error_reported.set(1); + }else{ + error_reported.reset(1); + } + + + if(R_STATUS_REG.BOCP) { + if(!error_reported.test(2)) THEKERNEL->streams->printf("%c, ERROR: Channel B over current shutdown\n", designator); + error= true; + error_reported.set(2); + }else{ + error_reported.reset(2); + } + + if(R_STATUS_REG.APDF) { + if(!error_reported.test(3)) THEKERNEL->streams->printf("%c, ERROR: Channel A predriver fault\n", designator); + error= true; + error_reported.set(3); + }else{ + error_reported.reset(3); + } + + + if(R_STATUS_REG.BPDF) { + if(!error_reported.test(4)) THEKERNEL->streams->printf("%c, ERROR: Channel B predriver fault\n", designator); + error= true; + error_reported.set(4); + }else{ + error_reported.reset(4); + } + + + return error; +} + + +// sets a raw register to the value specified, for advanced settings +// register 255 writes them, 0 displays what registers are mapped to what +bool DRV8711DRV::set_raw_register(StreamOutput *stream, uint32_t reg, uint32_t val) +{ + switch(reg) { + case 255: + WriteAllRegisters(); + stream->printf("Registers written\n"); + break; + + case 1: G_CTRL_REG.raw &= 0xF000; G_CTRL_REG.raw |= (val & 0x0FFF); break; + case 2: G_TORQUE_REG.raw &= 0xF000; G_TORQUE_REG.raw |= (val & 0x0FFF); break; + case 3: G_OFF_REG.raw &= 0xF000; G_OFF_REG.raw |= (val & 0x0FFF); break; + case 4: G_BLANK_REG.raw &= 0xF000; G_BLANK_REG.raw |= (val & 0x0FFF); break; + case 5: G_DECAY_REG.raw &= 0xF000; G_DECAY_REG.raw |= (val & 0x0FFF); break; + case 6: G_STALL_REG.raw &= 0xF000; G_STALL_REG.raw |= (val & 0x0FFF); break; + case 7: G_DRIVE_REG.raw &= 0xF000; G_DRIVE_REG.raw |= (val & 0x0FFF); break; + + default: + stream->printf("1: CTRL Register\n"); + stream->printf("2: TORQUE Register\n"); + stream->printf("3: OFF Register\n"); + stream->printf("4: BLANK Register\n"); + stream->printf("5: DECAY Register\n"); + stream->printf("6: STALL Register\n"); + stream->printf("7: DRIVE Register\n"); + stream->printf("255: write registers to chip\n"); + return false; + } + return true; +} + +uint16_t DRV8711DRV::ReadRegister(uint8_t addr) +{ + return ReadWriteRegister(REGREAD | (addr << 4), 0); +} + +uint16_t DRV8711DRV::ReadWriteRegister(uint8_t dataHi, uint8_t dataLo) +{ + uint8_t buf[2] {dataHi, dataLo}; + uint8_t rbuf[2]; + + spi(buf, 2, rbuf); + //THEKERNEL->streams->printf("sent: %02X, %02X received:%02X, %02X\n", buf[0], buf[1], rbuf[0], rbuf[1]); + uint16_t readData = (rbuf[0] << 8) | rbuf[1]; + return readData; +} + +#if 0 +#define HAS(X) (options.find(X) != options.end()) +#define GET(X) (options.at(X)) +bool DRV8711DRV::set_options(const options_t& options) +{ + bool set = false; + if(HAS('O') || HAS('Q')) { + // void TMC26X::setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled) + int8_t o = HAS('O') ? GET('O') : getStallGuardThreshold(); + int8_t q = HAS('Q') ? GET('Q') : getStallGuardFilter(); + setStallGuardThreshold(o, q); + set = true; + } + + if(HAS('S')) { + uint32_t s = GET('S'); + if(s == 0 && HAS('U') && HAS('V') && HAS('W') && HAS('X') && HAS('Y')) { + //void TMC26X::setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator) + setConstantOffTimeChopper(GET('U'), GET('V'), GET('W'), GET('X'), GET('Y')); + set = true; + + } else if(s == 2 && HAS('Z')) { + setRandomOffTime(GET('Z')); + set = true; + } + } + + return set; +} +#endif diff --git a/src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.h b/src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.h new file mode 100644 index 00000000..0b893bfb --- /dev/null +++ b/src/modules/utils/motordrivercontrol/drivers/DRV8711/drv8711.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include + +class StreamOutput; + +#define DRV8711_max_current 4000 + +class DRV8711DRV +{ +public: + DRV8711DRV(std::function spi, char designator); + + void init(uint16_t cs) ; + + void set_enable(bool enable) ; + int set_microsteps(int number_of_steps); + void set_current(uint32_t currentma); + + void dump_status(StreamOutput *stream) ; + bool set_raw_register(StreamOutput *stream, uint32_t reg, uint32_t val); + bool check_alarm(); + +private: + + uint16_t ReadWriteRegister(uint8_t dataHi, uint8_t dataLo); + uint16_t ReadRegister(uint8_t addr); + void ReadAllRegisters () ; + void WriteAllRegisters () ; + +// WARNING this may not be portable, and endianess affects the order, but it works for LPC1769 +// CTRL Register + typedef union { + struct { + uint8_t ENBL: 1; // bit 0 + uint8_t RDIR: 1; // bit 1 + uint8_t RSTEP: 1; // bit 2 + uint8_t MODE: 4; // bits 6-3 + uint8_t EXSTALL: 1; // bit 7 + uint8_t ISGAIN: 2; // bits 9-8 + uint8_t DTIME: 2; // bits 11-10 + uint8_t Address: 3; // bits 14-12 + }; + uint16_t raw; + } CTRL_Register_t; + +// TORQUE Register + typedef union { + struct { + uint8_t TORQUE: 8; // bits 7-0 + uint8_t SIMPLTH: 3; // bits 10-8 + uint8_t Reserved: 1; // bit 11 + uint8_t Address: 3; // bits 14-12 + }; + uint16_t raw; + } TORQUE_Register_t; + +// OFF Register + typedef union { + struct { + uint8_t TOFF: 8; // bits 7-0 + uint8_t PWMMODE: 1; // bit 8 + uint8_t Reserved: 3; // bits 11-9 + uint8_t Address: 3; // bits 14-12 + }; + uint16_t raw; + } OFF_Register_t; + +// BLANK Register + typedef union { + struct { + uint8_t TBLANK:8; // bits 7-0 + uint8_t ABT:1; // bit 8 + uint8_t Reserved:3; // bits 11-9 + uint8_t Address:3; // bits 14-12 + }; + uint16_t raw; + } BLANK_Register_t; + +// DECAY Register + typedef union { + struct { + uint8_t TDECAY:8; // bits 7-0 + uint8_t DECMOD:3; // bits 10-8 + uint8_t Reserved:1; // bit 11 + uint8_t Address:3; // bits 14-12 + }; + uint16_t raw; + } DECAY_Register_t; + +// STALL Register + typedef union { + struct { + uint8_t SDTHR:8; // bits 7-0 + uint8_t SDCNT:2; // bits 9-8 + uint8_t VDIV:2; // bits 11-10 + uint8_t Address:3; // bits 14-12 + }; + uint16_t raw; + } STALL_Register_t; + +// DRIVE Register + typedef union { + struct { + uint8_t OCPTH:2; // bits 1-0 + uint8_t OCPDEG:2; // bits 3-2 + uint8_t TDRIVEN:2; // bits 5-4 + uint8_t TDRIVEP:2; // bits 7-6 + uint8_t IDRIVEN:2; // bits 9-8 + uint8_t IDRIVEP:2; // bits 11-10 + uint8_t Address:3; // bits 14-12 + }; + uint16_t raw; + } DRIVE_Register_t; + +// STATUS Register + typedef union { + struct { + uint8_t OTS:1; // bit 0 + uint8_t AOCP:1; // bit 1 + uint8_t BOCP:1; // bit 2 + uint8_t APDF:1; // bit 3 + uint8_t BPDF:1; // bit 4 + uint8_t UVLO:1; // bit 5 + uint8_t STD:1; // bit 6 + uint8_t STDLAT:1; // bit 7 + uint8_t Reserved:4; // bits 11-8 + uint8_t Address:3; // bits 14-12 + }; + uint16_t raw; + } STATUS_Register_t; + + CTRL_Register_t G_CTRL_REG; + TORQUE_Register_t G_TORQUE_REG; + OFF_Register_t G_OFF_REG; + BLANK_Register_t G_BLANK_REG; + DECAY_Register_t G_DECAY_REG; + STALL_Register_t G_STALL_REG; + DRIVE_Register_t G_DRIVE_REG; + STATUS_Register_t G_STATUS_REG; + + std::function spi; + float resistor{0.05}; + std::bitset<8> error_reported; + uint8_t gain{20}; + char designator; + + // float _amps; + // uint8_t _microstepreg; +}; diff --git a/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp b/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp new file mode 100644 index 00000000..ee64570d --- /dev/null +++ b/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp @@ -0,0 +1,1380 @@ +/* + Highly modified from.... + + TMC21X.cpp - - TMC21X Stepper library for Wiring/Arduino + + based on the stepper library by Tom Igoe, et. al. + + Copyright (c) 2011, Interactive Matter, Marcus Nowotny + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + */ + +#include "TMC21X.h" +#include "mbed.h" +#include "StreamOutput.h" +#include "Kernel.h" +#include "libs/StreamOutputPool.h" +#include "Robot.h" +#include "StepperMotor.h" +#include "ConfigValue.h" +#include "Config.h" +#include "checksumm.h" + + +#define motor_driver_control_checksum CHECKSUM("motor_driver_control") +#define sense_resistor_checksum CHECKSUM("sense_resistor") + +//! return value for TMC21X.getOverTemperature() if there is a over temperature situation in the TMC chip +/*! + * This warning indicates that the TCM chip is too warm. + * It is still working but some parameters may be inferior. + * You should do something against it. + */ +#define TMC21X_OVERTEMPERATURE_PREWARING 1 +//! return value for TMC21X.getOverTemperature() if there is a over temperature shutdown in the TMC chip +/*! + * This warning indicates that the TCM chip is too warm to operate and has shut down to prevent damage. + * It will stop working until it cools down again. + * If you encounter this situation you must do something against it. Like reducing the current or improving the PCB layout + * and/or heat management. + */ +#define TMC21X_OVERTEMPERATURE_SHUTDOWN 2 + +//which values can be read out +/*! + * Selects to readout the microstep position from the motor. + *\sa readStatus() + */ +#define TMC21X_READOUT_POSITION 0 +/*! + * Selects to read out the StallGuard value of the motor. + *\sa readStatus() + */ +#define TMC21X_READOUT_STALLGUARD 1 +/*! + * Selects to read out the current current setting (acc. to CoolStep) and the upper bits of the StallGuard value from the motor. + *\sa readStatus(), setCurrent() + */ +#define TMC21X_READOUT_CURRENT 3 + +/*! + * Define to set the minimum current for CoolStep operation to 1/2 of the selected CS minium. + *\sa setCoolStepConfiguration() + */ +#define COOL_STEP_HALF_CS_LIMIT 0 +/*! + * Define to set the minimum current for CoolStep operation to 1/4 of the selected CS minimum. + *\sa setCoolStepConfiguration() + */ +#define COOL_STEP_QUARTDER_CS_LIMIT 1 + + +//some default values used in initialization +#define DEFAULT_MICROSTEPPING_VALUE 32 + +//TMC2130 read / write mask +#define TMC_WRITE_notREAD 0x80 //write flag + +//TMC2130 registers definitions // R = Read +// // W = Write +// // RC = Read +// // and Clear + // Number // + // of Bits// +#define REG_GCONF 0x00 // RW // 17 //Global configuration flags +#define REG_GSTAT 0x01 // RC // 3 //Global status flags +#define REG_IOIN 0x04 // R // 8+8 //The state of all input pins + //VELOCITY DEPENDENT DRIVER FEATURE CONTROL REGISTER SET (0X10…0X1F) +#define REG_IHOLD_IRUN 0x10 // W // 5+5+4 //Driver current control +#define REG_TPOWERDOWN 0x11 // W // 8 // sets the delay time after stand still (stst) of the motor to motor current power down. 8 bits + //Time range is about 0 to 4 seconds. 0…((2^8)-1) * 2^18 tCLK +#define REG_TSTEP 0x12 // R // 20 //Actual measured time between two 1/256 microsteps derived from the step input frequency. 20 bits + //In units of 1/fCLK. Measured value is (2^20)-1 in case of overflow or stand still. + //All TSTEP related thresholds use a hysteresis of 1/16 of the compare value to compensate for jitter in the clock or the step frequency. + //The flag small_hysteresis modifies the hysteresis to a smaller value of 1/32. + //(Txxx*15/16)-1 or (Txxx*31/32)-1 is used as a second compare value for each comparison value. + //This means, that the lower switching velocity equals the calculated setting, + //but the upper switching velocity is higher as defined by the hysteresis setting. + //In dcStep mode TSTEP will not show the mean velocity of the motor, but the velocities for each microstep, + //which may not be stable and thus does not represent the real motor velocity in case it runs slower than the target velocity. +#define REG_TPWMTHRS 0x13 // W // 20 //This is the upper velocity for stealthChop voltage PWM mode. + //TSTEP ≄ TPWMTHRS + // - stealthChop PWM mode is enabled, if configured + // - dcStep is disabled +#define REG_TCOOLTHRS 0x14 // W // 20 //This is the lower threshold velocity for switching on smart energy coolStep and stallGuard feature. + // (unsigned) + //Set this parameter to disable coolStep at low speeds, where it cannot work reliably. + //The stall detection and stallGuard output signal becomes enabled when exceeding this velocity. + //In non-dcStep mode, it becomes disabled again once the velocity falls below this threshold. + //TCOOLTHRS ≄ TSTEP ≄ THIGH: + // - coolStep is enabled, if configured + // - stealthChop voltage PWM mode is disabled + //TCOOLTHRS ≄ TSTEP + // - Stop on stall and stall output signal is enabled, if configured +#define REG_THIGH 0x15 // W // 20 //This velocity setting allows velocity dependent switching into a different chopper mode and fullstepping to maximize torque. + // (unsigned) + //The stall detection feature becomes switched off for 2-3 electrical periods whenever passing THIGH threshold to compensate for the effect of switching modes. + //TSTEP ≤ THIGH: + // - coolStep is disabled (motor runs with normal current scale) + // - stealthChop voltage PWM mode is disabled + // - If vhighchm is set, the chopper switches to chm=1 with TFD=0 (constant off time with slow decay, only). + // - chopSync2 is switched off (SYNC=0) + // - If vhighfs is set, the motor operates in fullstep mode and the stall detection becomes switched over to dcStep stall detection. + // + //NOTE: microstep velocity time reference t for velocities: TSTEP = fCLK / fSTEP +#define REG_XDIRECT 0x2D // RW // 32 // SPI Mode Register + //0: Normal operation + //1: Directly SPI driven motor current + // Direct mode operation: + // XDIRECT specifies Motor coil currents and polarity directly programmed via the serial interface. Use signed, two’s complement numbers. + // Coil A current (bits 8..0) (signed) + // Coil B current (bits 24..16) (signed) + // Range: +-248 for normal operation, up to +-255 with stealthChop + //In this mode, the current is scaled by IHOLD setting. + //Velocity based current regulation of voltage PWM is not available in this mode. + //The automatic voltage PWM current regulation will work only for low stepper motor velocities. + //dcStep is not available in this mode. coolStep and stallGuard only can be used, when additionally supplying a STEP signal. + //This will also enable automatic current scaling. +#define REG_VDCMIN 0x33 // W // 23 //dcStep Minimum Velocity Register + //The automatic commutation dcStep becomes enabled by the external signal DCEN. + //VDCMIN is used as the minimum step velocity when the motor is heavily loaded. + //Hint: Also set DCCTRL parameters in order to operate dcStep. + // + // MICROSTEPPING CONTROL REGISTER SET (0X60…0X6B) + //Each bit gives the difference between entry x and entry x+1 when combined with the + //corresponding MSLUTSEL W bits: + //0: W= %00: -1 + // %01: +0 + // %10: +1 + // %11: +2 + // 1: W= %00: +0 + // %01: +1 + // %10: +2 + // %11: +3 + // This is the differential coding for the first quarter of a wave. + // Start values for CUR_A and CUR_B are stored for MSCNT position 0 in + // START_SIN and START_SIN90. + // ofs31, ofs30, …, ofs01, ofs00 + // … + // ofs255, ofs254, …, ofs225, ofs224 +#define REG_MSLUT0 0x60 // W // 32 // Bits 0..31 - 1 x 32 bits of 0 or 1 reset default= sine wave table +#define REG_MSLUT1 0x61 // W // 32 // Bits 7 x 32 bits of 0 or 1 reset default= sine wave table +#define REG_MSLUT2 0x62 // W // 32 // 32 +#define REG_MSLUT3 0x63 // W // 32 // . +#define REG_MSLUT4 0x64 // W // 32 // . +#define REG_MSLUT5 0x65 // W // 32 // . +#define REG_MSLUT6 0x66 // W // 32 // +#define REG_MSLUT7 0x67 // W // 32 // 255 +#define REG_MSLUTSEL 0x68 // W // 32 //This register defines four segments within each quarter MSLUT wave. + //Four 2 bit entries determine the meaning of a 0 and a 1 bit in the corresponding segment of MSLUT. + //Range: 0 spi, char d) : spi(spi), designator(d) //TODO Convert +{ + //we are not started yet + started = false; + //by default cool step is not enabled + cool_step_enabled = false; + error_reported.reset(); +} + +/* + * configure the stepper driver + * just must be called. + */ +void TMC21X::init(uint16_t cs) //TODO Convert +{ + // read chip specific config entries + this->resistor= THEKERNEL->config->value(motor_driver_control_checksum, cs, sense_resistor_checksum)->by_default(50)->as_number(); // in milliohms + + // Initialize the TMC2130 + //TODO need to generalize this so any device using SPI will work + //TODO need to add semaphores or similar to prevent more than one SPI call happening at once + //set TMC2130 config + this->send2130(TMC_WRITE_notREAD|REG_GCONF, 0x00000001UL); //voltage on AIN is current reference + this->send2130(TMC_WRITE_notREAD|REG_IHOLD_IRUN, 0x00001010UL); //IHOLD=0x10, IRUN=0x10 + this->send2130(TMC_WRITE_notREAD|REG_CHOPCONF, 0x00008008UL); //native 256 microsteps, MRES=0, TBL=1=24, TOFF=8 + + + + /* + //setting the default register values + driver_control_register_value = DRIVER_CONTROL_REGISTER; + chopper_config_register = CHOPPER_CONFIG_REGISTER; + cool_step_register_value = COOL_STEP_REGISTER; + stall_guard2_current_register_value = STALL_GUARD2_LOAD_MEASURE_REGISTER; + driver_configuration_register_value = DRIVER_CONFIG_REGISTER | READ_STALL_GUARD_READING; + + //set the initial values + + + + send2130_old(driver_control_register_value); + send2130_old(chopper_config_register); + send2130_old(cool_step_register_value); + send2130_old(stall_guard2_current_register_value); + send2130_old(driver_configuration_register_value); +*/ + started = true; +/* +#if 1 + //set to a conservative start value + setConstantOffTimeChopper(7, 54, 13, 12, 1); +#else + // for 1.5amp kysan @ 12v + setSpreadCycleChopper(5, 54, 5, 0, 0); + // for 4amp Nema24 @ 12v + //setSpreadCycleChopper(5, 54, 4, 0, 0); +#endif +*/ + setEnabled(false); + + //set a nice microstepping value +// setMicrosteps(DEFAULT_MICROSTEPPING_VALUE); + + // set stallguard to a conservative value so it doesn't trigger immediately +// setStallGuardThreshold(10, 1); +} + +void TMC21X::setCurrent(unsigned int current) //TODO Convert +{ + uint8_t current_scaling = 0; + //calculate the current scaling from the max current setting (in mA) + double mASetting = (double)current; + double resistor_value = (double) this->resistor; + // remove vesense flag + this->driver_configuration_register_value &= ~(VSENSE); + //this is derrived from I=(cs+1)/32*(Vsense/Rsense) + //leading to cs = CS = 32*R*I/V (with V = 0,31V oder 0,165V and I = 1000*current) + //with Rsense=0,15 + //for vsense = 0,310V (VSENSE not set) + //or vsense = 0,165V (VSENSE set) + current_scaling = (uint8_t)((resistor_value * mASetting * 32.0F / (0.31F * 1000.0F * 1000.0F)) - 0.5F); //theoretically - 1.0 for better rounding it is 0.5 + + //check if the current scaling is too low + if (current_scaling < 16) { + //set the csense bit to get a use half the sense voltage (to support lower motor currents) + this->driver_configuration_register_value |= VSENSE; + //and recalculate the current setting + current_scaling = (uint8_t)((resistor_value * mASetting * 32.0F / (0.165F * 1000.0F * 1000.0F)) - 0.5F); //theoretically - 1.0 for better rounding it is 0.5 + } + + //do some sanity checks + if (current_scaling > 31) { + current_scaling = 31; + } + //delete the old value + stall_guard2_current_register_value &= ~(CURRENT_SCALING_PATTERN); + //set the new current scaling + stall_guard2_current_register_value |= current_scaling; + //if started we directly send it to the motor + if (started) { + send2130_old(driver_configuration_register_value); + send2130_old(stall_guard2_current_register_value); + } +} + +unsigned int TMC21X::getCurrent(void) //TODO Convert +{ + + //we calculate the current according to the datasheet to be on the safe side + //this is not the fastest but the most accurate and illustrative way + double result = (double)(stall_guard2_current_register_value & CURRENT_SCALING_PATTERN); + double resistor_value = (double)this->resistor; + double voltage = (driver_configuration_register_value & VSENSE) ? 0.165F : 0.31F; + result = (result + 1.0F) / 32.0F * voltage / resistor_value * 1000.0F * 1000.0F; + return (unsigned int)result; +} + +void TMC21X::setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled) //TODO Convert +{ + if (stall_guard_threshold < -64) { + stall_guard_threshold = -64; + //We just have 5 bits + } else if (stall_guard_threshold > 63) { + stall_guard_threshold = 63; + } + //add trim down to 7 bits + stall_guard_threshold &= 0x7f; + //delete old stall guard settings + stall_guard2_current_register_value &= ~(STALL_GUARD_CONFIG_PATTERN); + if (stall_guard_filter_enabled) { + stall_guard2_current_register_value |= STALL_GUARD_FILTER_ENABLED; + } + //Set the new stall guard threshold + stall_guard2_current_register_value |= (((unsigned long)stall_guard_threshold << 8) & STALL_GUARD_CONFIG_PATTERN); + //if started we directly send it to the motor + if (started) { + send2130_old(stall_guard2_current_register_value); + } +} + +int8_t TMC21X::getStallGuardThreshold(void) //TODO Convert +{ + unsigned long stall_guard_threshold = stall_guard2_current_register_value & STALL_GUARD_VALUE_PATTERN; + //shift it down to bit 0 + stall_guard_threshold >>= 8; + //convert the value to an int to correctly handle the negative numbers + int8_t result = stall_guard_threshold; + //check if it is negative and fill it up with leading 1 for proper negative number representation + if (result & (1 << 6)) { + result |= 0xC0; + } + return result; +} + +int8_t TMC21X::getStallGuardFilter(void) //TODO Convert +{ + if (stall_guard2_current_register_value & STALL_GUARD_FILTER_ENABLED) { + return -1; + } else { + return 0; + } +} +/* + * Set the number of microsteps per step. + * 0,2,4,8,16,32,64,128,256 is supported + * any value in between will be mapped to the next smaller value + * 0 and 1 set the motor in full step mode + */ +void TMC21X::setMicrosteps(int number_of_steps) //TODO Convert +{ + long setting_pattern; + //poor mans log + if (number_of_steps >= 256) { + setting_pattern = 0; + microsteps = 256; + } else if (number_of_steps >= 128) { + setting_pattern = 1; + microsteps = 128; + } else if (number_of_steps >= 64) { + setting_pattern = 2; + microsteps = 64; + } else if (number_of_steps >= 32) { + setting_pattern = 3; + microsteps = 32; + } else if (number_of_steps >= 16) { + setting_pattern = 4; + microsteps = 16; + } else if (number_of_steps >= 8) { + setting_pattern = 5; + microsteps = 8; + } else if (number_of_steps >= 4) { + setting_pattern = 6; + microsteps = 4; + } else if (number_of_steps >= 2) { + setting_pattern = 7; + microsteps = 2; + //1 and 0 lead to full step + } else if (number_of_steps <= 1) { + setting_pattern = 8; + microsteps = 1; + } + + //delete the old value + this->driver_control_register_value &= 0xFFFF0ul; + //set the new value + this->driver_control_register_value |= setting_pattern; + + //if started we directly send it to the motor + if (started) { + send2130_old(driver_control_register_value); + } +} + +/* + * returns the effective number of microsteps at the moment + */ +int TMC21X::getMicrosteps(void) //TODO Convert +{ + return microsteps; +} + +void TMC21X::setStepInterpolation(int8_t value) +{ + if (value) { + driver_control_register_value |= STEP_INTERPOLATION; + } else { + driver_control_register_value &= ~(STEP_INTERPOLATION); + } + //if started we directly send it to the motor + if (started) { + send2130_old(driver_control_register_value); + } +} + +void TMC21X::setDoubleEdge(int8_t value) //TODO Convert +{ + if (value) { + driver_control_register_value |= DOUBLE_EDGE_STEP; + } else { + driver_control_register_value &= ~(DOUBLE_EDGE_STEP); + } + //if started we directly send it to the motor + if (started) { + send2130_old(driver_control_register_value); + } +} + +/* + * constant_off_time: The off time setting controls the minimum chopper frequency. + * For most applications an off time within the range of 5μs to 20μs will fit. + * 2...15: off time setting + * + * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the + * duration of the ringing on the sense resistor. For + * 0: min. setting 3: max. setting + * + * fast_decay_time_setting: Fast decay time setting. With CHM=1, these bits control the portion of fast decay for each chopper cycle. + * 0: slow decay only + * 1...15: duration of fast decay phase + * + * sine_wave_offset: Sine wave offset. With CHM=1, these bits control the sine wave offset. + * A positive offset corrects for zero crossing error. + * -3..-1: negative offset 0: no offset 1...12: positive offset + * + * use_current_comparator: Selects usage of the current comparator for termination of the fast decay cycle. + * If current comparator is enabled, it terminates the fast decay cycle in case the current + * reaches a higher negative value than the actual positive value. + * 1: enable comparator termination of fast decay cycle + * 0: end by time only + */ +void TMC21X::setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator) //TODO Convert +{ + //perform some sanity checks + if (constant_off_time < 2) { + constant_off_time = 2; + } else if (constant_off_time > 15) { + constant_off_time = 15; + } + //save the constant off time + this->constant_off_time = constant_off_time; + int8_t blank_value; + //calculate the value acc to the clock cycles + if (blank_time >= 54) { + blank_value = 3; + } else if (blank_time >= 36) { + blank_value = 2; + } else if (blank_time >= 24) { + blank_value = 1; + } else { + blank_value = 0; + } + this->blank_time = blank_time; + + if (fast_decay_time_setting < 0) { + fast_decay_time_setting = 0; + } else if (fast_decay_time_setting > 15) { + fast_decay_time_setting = 15; + } + if (sine_wave_offset < -3) { + sine_wave_offset = -3; + } else if (sine_wave_offset > 12) { + sine_wave_offset = 12; + } + //shift the sine_wave_offset + sine_wave_offset += 3; + + //calculate the register setting + //first of all delete all the values for this + chopper_config_register &= ~((1 << 12) | BLANK_TIMING_PATTERN | HYSTERESIS_DECREMENT_PATTERN | HYSTERESIS_LOW_VALUE_PATTERN | HYSTERESIS_START_VALUE_PATTERN | T_OFF_TIMING_PATERN); + //set the constant off pattern + chopper_config_register |= CHOPPER_MODE_T_OFF_FAST_DECAY; + //set the blank timing value + chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT; + //setting the constant off time + chopper_config_register |= constant_off_time; + //set the fast decay time + //set msb + chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x8)) << HYSTERESIS_DECREMENT_SHIFT); + //other bits + chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x7)) << HYSTERESIS_START_VALUE_SHIFT); + //set the sine wave offset + chopper_config_register |= (unsigned long)sine_wave_offset << HYSTERESIS_LOW_SHIFT; + //using the current comparator? + if (!use_current_comparator) { + chopper_config_register |= (1 << 12); + } + //if started we directly send it to the motor + if (started) { + send2130_old(chopper_config_register); + } +} + +/* + * constant_off_time: The off time setting controls the minimum chopper frequency. + * For most applications an off time within the range of 5μs to 20μs will fit. + * 2...15: off time setting + * + * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the + * duration of the ringing on the sense resistor. For + * 0: min. setting 3: max. setting + * + * hysteresis_start: Hysteresis start setting. Please remark, that this value is an offset to the hysteresis end value HEND. + * 1...8 + * + * hysteresis_end: Hysteresis end setting. Sets the hysteresis end value after a number of decrements. Decrement interval time is controlled by HDEC. + * The sum HSTRT+HEND must be <16. At a current setting CS of max. 30 (amplitude reduced to 240), the sum is not limited. + * -3..-1: negative HEND 0: zero HEND 1...12: positive HEND + * + * hysteresis_decrement: Hysteresis decrement setting. This setting determines the slope of the hysteresis during on time and during fast decay time. + * 0: fast decrement 3: very slow decrement + */ + +void TMC21X::setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement) //TODO Convert +{ + h_start = hysteresis_start; + h_end = hysteresis_end; + h_decrement = hysteresis_decrement; + this->blank_time = blank_time; + + //perform some sanity checks + if (constant_off_time < 2) { + constant_off_time = 2; + } else if (constant_off_time > 15) { + constant_off_time = 15; + } + //save the constant off time + this->constant_off_time = constant_off_time; + int8_t blank_value; + //calculate the value acc to the clock cycles + if (blank_time >= 54) { + blank_value = 3; + } else if (blank_time >= 36) { + blank_value = 2; + } else if (blank_time >= 24) { + blank_value = 1; + } else { + blank_value = 0; + } + + if (hysteresis_start < 1) { + hysteresis_start = 1; + } else if (hysteresis_start > 8) { + hysteresis_start = 8; + } + hysteresis_start--; + + if (hysteresis_end < -3) { + hysteresis_end = -3; + } else if (hysteresis_end > 12) { + hysteresis_end = 12; + } + //shift the hysteresis_end + hysteresis_end += 3; + + if (hysteresis_decrement < 0) { + hysteresis_decrement = 0; + } else if (hysteresis_decrement > 3) { + hysteresis_decrement = 3; + } + + //first of all delete all the values for this + chopper_config_register &= ~(CHOPPER_MODE_T_OFF_FAST_DECAY | BLANK_TIMING_PATTERN | HYSTERESIS_DECREMENT_PATTERN | HYSTERESIS_LOW_VALUE_PATTERN | HYSTERESIS_START_VALUE_PATTERN | T_OFF_TIMING_PATERN); + + //set the blank timing value + chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT; + //setting the constant off time + chopper_config_register |= constant_off_time; + //set the hysteresis_start + chopper_config_register |= ((unsigned long)hysteresis_start) << HYSTERESIS_START_VALUE_SHIFT; + //set the hysteresis end + chopper_config_register |= ((unsigned long)hysteresis_end) << HYSTERESIS_LOW_SHIFT; + //set the hystereis decrement + chopper_config_register |= ((unsigned long)hysteresis_decrement) << HYSTERESIS_DECREMENT_SHIFT; + + //if started we directly send it to the motor + if (started) { + send2130_old(chopper_config_register); + } +} + +/* + * In a constant off time chopper scheme both coil choppers run freely, i.e. are not synchronized. + * The frequency of each chopper mainly depends on the coil current and the position dependant motor coil inductivity, thus it depends on the microstep position. + * With some motors a slightly audible beat can occur between the chopper frequencies, especially when they are near to each other. This typically occurs at a + * few microstep positions within each quarter wave. This effect normally is not audible when compared to mechanical noise generated by ball bearings, etc. + * Further factors which can cause a similar effect are a poor layout of sense resistor GND connection. + * Hint: A common factor, which can cause motor noise, is a bad PCB layout causing coupling of both sense resistor voltages + * (please refer to sense resistor layout hint in chapter 8.1). + * In order to minimize the effect of a beat between both chopper frequencies, an internal random generator is provided. + * It modulates the slow decay time setting when switched on by the RNDTF bit. The RNDTF feature further spreads the chopper spectrum, + * reducing electromagnetic emission on single frequencies. + */ +void TMC21X::setRandomOffTime(int8_t value) //TODO Convert +{ + if (value) { + chopper_config_register |= RANDOM_TOFF_TIME; + } else { + chopper_config_register &= ~(RANDOM_TOFF_TIME); + } + //if started we directly send it to the motor + if (started) { + send2130_old(chopper_config_register); + } +} + +void TMC21X::setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size, + uint8_t current_increment_step_size, uint8_t lower_current_limit) //TODO Convert +{ + //sanitize the input values + if (lower_SG_threshold > 480) { + lower_SG_threshold = 480; + } + //divide by 32 + lower_SG_threshold >>= 5; + if (SG_hysteresis > 480) { + SG_hysteresis = 480; + } + //divide by 32 + SG_hysteresis >>= 5; + if (current_decrement_step_size > 3) { + current_decrement_step_size = 3; + } + if (current_increment_step_size > 3) { + current_increment_step_size = 3; + } + if (lower_current_limit > 1) { + lower_current_limit = 1; + } + //store the lower level in order to enable/disable the cool step + this->cool_step_lower_threshold = lower_SG_threshold; + //if cool step is not enabled we delete the lower value to keep it disabled + if (!this->cool_step_enabled) { + lower_SG_threshold = 0; + } + //the good news is that we can start with a complete new cool step register value + //and simply set the values in the register + cool_step_register_value = ((unsigned long)lower_SG_threshold) | (((unsigned long)SG_hysteresis) << 8) | (((unsigned long)current_decrement_step_size) << 5) + | (((unsigned long)current_increment_step_size) << 13) | (((unsigned long)lower_current_limit) << 15) + //and of course we have to include the signature of the register + | COOL_STEP_REGISTER; + + if (started) { + send2130_old(cool_step_register_value); + } +} + +void TMC21X::setCoolStepEnabled(bool enabled) //TODO Convert +{ + //simply delete the lower limit to disable the cool step + cool_step_register_value &= ~SE_MIN_PATTERN; + //and set it to the proper value if cool step is to be enabled + if (enabled) { + cool_step_register_value |= this->cool_step_lower_threshold; + } + //and save the enabled status + this->cool_step_enabled = enabled; + //save the register value + if (started) { + send2130_old(cool_step_register_value); + } +} + +bool TMC21X::isCoolStepEnabled(void) //TODO Convert +{ + return this->cool_step_enabled; +} + +unsigned int TMC21X::getCoolStepLowerSgThreshold() //TODO Convert +{ + //we return our internally stored value - in order to provide the correct setting even if cool step is not enabled + return this->cool_step_lower_threshold << 5; +} + +unsigned int TMC21X::getCoolStepUpperSgThreshold() //TODO Convert +{ + return (uint8_t)((cool_step_register_value & SE_MAX_PATTERN) >> 8) << 5; +} + +uint8_t TMC21X::getCoolStepCurrentIncrementSize() //TODO Convert +{ + return (uint8_t)((cool_step_register_value & CURRENT_DOWN_STEP_SPEED_PATTERN) >> 13); +} + +uint8_t TMC21X::getCoolStepNumberOfSGReadings() //TODO Convert +{ + return (uint8_t)((cool_step_register_value & SE_CURRENT_STEP_WIDTH_PATTERN) >> 5); +} + +uint8_t TMC21X::getCoolStepLowerCurrentLimit() //TODO Convert +{ + return (uint8_t)((cool_step_register_value & MINIMUM_CURRENT_FOURTH) >> 15); +} + +void TMC21X::setEnabled(bool enabled) //TODO Convert +{ + //delete the t_off in the chopper config to get sure + chopper_config_register &= ~(T_OFF_PATTERN); + if (enabled) { + //and set the t_off time + chopper_config_register |= this->constant_off_time; + } + //if not enabled we don't have to do anything since we already delete t_off from the register + if (started) { + send2130_old(chopper_config_register); + } +} + +bool TMC21X::isEnabled() //TODO Convert +{ + if (chopper_config_register & T_OFF_PATTERN) { + return true; + } else { + return false; + } +} + +/* + * reads a value from the TMC21X status register. The value is not obtained directly but can then + * be read by the various status routines. + * + */ +void TMC21X::readStatus(int8_t read_value) //TODO Convert +{ + unsigned long old_driver_configuration_register_value = driver_configuration_register_value; + //reset the readout configuration + driver_configuration_register_value &= ~(READ_SELECTION_PATTERN); + //this now equals TMC21X_READOUT_POSITION - so we just have to check the other two options + if (read_value == TMC21X_READOUT_STALLGUARD) { + driver_configuration_register_value |= READ_STALL_GUARD_READING; + } else if (read_value == TMC21X_READOUT_CURRENT) { + driver_configuration_register_value |= READ_STALL_GUARD_AND_COOL_STEP; + } + //all other cases are ignored to prevent funny values + //check if the readout is configured for the value we are interested in + if (driver_configuration_register_value != old_driver_configuration_register_value) { + //because then we need to write the value twice - one time for configuring, second time to get the value, see below + send2130_old(driver_configuration_register_value); + } + //write the configuration to get the last status + send2130_old(driver_configuration_register_value); +} + +//reads the stall guard setting from last status +//returns -1 if stallguard information is not present +int TMC21X::getCurrentStallGuardReading(void) //TODO Convert +{ + //if we don't yet started there cannot be a stall guard value + if (!started) { + return -1; + } + //not time optimal, but solution optimal: + //first read out the stall guard value + readStatus(TMC21X_READOUT_STALLGUARD); + return getReadoutValue(); +} + +uint8_t TMC21X::getCurrentCSReading(void) //TODO Convert +{ + //if we don't yet started there cannot be a stall guard value + if (!started) { + return 0; + } + + //first read out the stall guard value + readStatus(TMC21X_READOUT_CURRENT); + return (getReadoutValue() & 0x1f); +} + +unsigned int TMC21X::getCoolstepCurrent(void) //TODO Convert +{ + float result = (float)getCurrentCSReading(); + float resistor_value = (float)this->resistor; + float voltage = (driver_configuration_register_value & VSENSE) ? 0.165F : 0.31F; + result = (result + 1.0F) / 32.0F * voltage / resistor_value * 1000.0F * 1000.0F; + return (unsigned int)roundf(result); +} + +/* + return true if the stallguard threshold has been reached +*/ +bool TMC21X::isStallGuardOverThreshold(void) //TODO Convert +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_STALL_GUARD_STATUS); +} + +/* + returns if there is any over temperature condition: + OVER_TEMPERATURE_PREWARING if pre warning level has been reached + OVER_TEMPERATURE_SHUTDOWN if the temperature is so hot that the driver is shut down + Any of those levels are not too good. +*/ +int8_t TMC21X::getOverTemperature(void) //TODO Convert +{ + if (!this->started) { + return 0; + } + if (driver_status_result & STATUS_OVER_TEMPERATURE_SHUTDOWN) { + return TMC21X_OVERTEMPERATURE_SHUTDOWN; + } + if (driver_status_result & STATUS_OVER_TEMPERATURE_WARNING) { + return TMC21X_OVERTEMPERATURE_PREWARING; + } + return 0; +} + +//is motor channel A shorted to ground +bool TMC21X::isShortToGroundA(void) //TODO Convert +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_SHORT_TO_GROUND_A); +} + +//is motor channel B shorted to ground +bool TMC21X::isShortToGroundB(void) //TODO Convert +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_SHORT_TO_GROUND_B); +} + +//is motor channel A connected +bool TMC21X::isOpenLoadA(void) //TODO Convert +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_OPEN_LOAD_A); +} + +//is motor channel B connected +bool TMC21X::isOpenLoadB(void) //TODO Convert +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_OPEN_LOAD_B); +} + +//is chopper inactive since 2^20 clock cycles - defaults to ~0,08s +bool TMC21X::isStandStill(void) //TODO Convert +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_STAND_STILL); +} + +//is chopper inactive since 2^20 clock cycles - defaults to ~0,08s +bool TMC21X::isStallGuardReached(void) //TODO Convert +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_STALL_GUARD_STATUS); +} + +//reads the stall guard setting from last status +//returns -1 if stallguard information is not present +int TMC21X::getReadoutValue(void) //TODO Convert +{ + return (int)(driver_status_result >> 10); +} + +bool TMC21X::isCurrentScalingHalfed() //TODO Convert +{ + if (this->driver_configuration_register_value & VSENSE) { + return true; + } else { + return false; + } +} + +void TMC21X::dumpStatus(StreamOutput *stream, bool readable) //TODO Convert +{ + if (readable) { + stream->printf("designator %c, Chip type TMC21X\n", designator); + + check_error_status_bits(stream); + + if (this->isStallGuardReached()) { + stream->printf("INFO: Stall Guard level reached!\n"); + } + + if (this->isStandStill()) { + stream->printf("INFO: Motor is standing still.\n"); + } + + int value = getReadoutValue(); + stream->printf("Microstep postion phase A: %d\n", value); + + value = getCurrentStallGuardReading(); + stream->printf("Stall Guard value: %d\n", value); + + stream->printf("Current setting: %dmA\n", getCurrent()); + stream->printf("Coolstep current: %dmA\n", getCoolstepCurrent()); + + stream->printf("Microsteps: 1/%d\n", microsteps); + + stream->printf("Register dump:\n"); + stream->printf(" driver control register: %08lX(%ld)\n", driver_control_register_value, driver_control_register_value); + stream->printf(" chopper config register: %08lX(%ld)\n", chopper_config_register, chopper_config_register); + stream->printf(" cool step register: %08lX(%ld)\n", cool_step_register_value, cool_step_register_value); + stream->printf(" stall guard2 current register: %08lX(%ld)\n", stall_guard2_current_register_value, stall_guard2_current_register_value); + stream->printf(" driver configuration register: %08lX(%ld)\n", driver_configuration_register_value, driver_configuration_register_value); + stream->printf(" motor_driver_control.xxx.reg %05lX,%05lX,%05lX,%05lX,%05lX\n", driver_control_register_value, chopper_config_register, cool_step_register_value, stall_guard2_current_register_value, driver_configuration_register_value); + + } else { + // TODO hardcoded for X need to select ABC as needed + bool moving = THEKERNEL->robot->actuators[0]->is_moving(); + // dump out in the format that the processing script needs + if (moving) { + stream->printf("#sg%d,p%lu,k%u,r,", getCurrentStallGuardReading(), THEKERNEL->robot->actuators[0]->get_stepped(), getCoolstepCurrent()); + } else { + readStatus(TMC21X_READOUT_POSITION); // get the status bits + stream->printf("#s,"); + } + stream->printf("d%d,", THEKERNEL->robot->actuators[0]->which_direction() ? 1 : -1); + stream->printf("c%u,m%d,", getCurrent(), getMicrosteps()); + // stream->printf('S'); + // stream->printf(tmc21XStepper.getSpeed(), DEC); + stream->printf("t%d,f%d,", getStallGuardThreshold(), getStallGuardFilter()); + + //print out the general cool step config + if (isCoolStepEnabled()) stream->printf("Ke+,"); + else stream->printf("Ke-,"); + + stream->printf("Kl%u,Ku%u,Kn%u,Ki%u,Km%u,", + getCoolStepLowerSgThreshold(), getCoolStepUpperSgThreshold(), getCoolStepNumberOfSGReadings(), getCoolStepCurrentIncrementSize(), getCoolStepLowerCurrentLimit()); + + //detect the winding status + if (isOpenLoadA()) { + stream->printf("ao,"); + } else if(isShortToGroundA()) { + stream->printf("ag,"); + } else { + stream->printf("a-,"); + } + //detect the winding status + if (isOpenLoadB()) { + stream->printf("bo,"); + } else if(isShortToGroundB()) { + stream->printf("bg,"); + } else { + stream->printf("b-,"); + } + + char temperature = getOverTemperature(); + if (temperature == 0) { + stream->printf("x-,"); + } else if (temperature == TMC21X_OVERTEMPERATURE_PREWARING) { + stream->printf("xw,"); + } else { + stream->printf("xe,"); + } + + if (isEnabled()) { + stream->printf("e1,"); + } else { + stream->printf("e0,"); + } + + //write out the current chopper config + stream->printf("Cm%d,", (chopper_config_register & CHOPPER_MODE_T_OFF_FAST_DECAY) != 0); + stream->printf("Co%d,Cb%d,", constant_off_time, blank_time); + if ((chopper_config_register & CHOPPER_MODE_T_OFF_FAST_DECAY) == 0) { + stream->printf("Cs%d,Ce%d,Cd%d,", h_start, h_end, h_decrement); + } + stream->printf("\n"); + } +} + +// check error bits and report, only report once +bool TMC21X::check_error_status_bits(StreamOutput *stream) //TODO Convert +{ + bool error= false; + readStatus(TMC21X_READOUT_POSITION); // get the status bits + + if (this->getOverTemperature()&TMC21X_OVERTEMPERATURE_PREWARING) { + if(!error_reported.test(0)) stream->printf("%c - WARNING: Overtemperature Prewarning!\n", designator); + error_reported.set(0); + }else{ + error_reported.reset(0); + } + + if (this->getOverTemperature()&TMC21X_OVERTEMPERATURE_SHUTDOWN) { + if(!error_reported.test(1)) stream->printf("%c - ERROR: Overtemperature Shutdown!\n", designator); + error=true; + error_reported.set(1); + }else{ + error_reported.reset(1); + } + + if (this->isShortToGroundA()) { + if(!error_reported.test(2)) stream->printf("%c - ERROR: SHORT to ground on channel A!\n", designator); + error=true; + error_reported.set(2); + }else{ + error_reported.reset(2); + } + + if (this->isShortToGroundB()) { + if(!error_reported.test(3)) stream->printf("%c - ERROR: SHORT to ground on channel B!\n", designator); + error=true; + error_reported.set(3); + }else{ + error_reported.reset(3); + } + + // these seem to be triggered when moving so ignore them for now + if (this->isOpenLoadA()) { + if(!error_reported.test(4)) stream->printf("%c - ERROR: Channel A seems to be unconnected!\n", designator); + error=true; + error_reported.set(4); + }else{ + error_reported.reset(4); + } + + if (this->isOpenLoadB()) { + if(!error_reported.test(5)) stream->printf("%c - ERROR: Channel B seems to be unconnected!\n", designator); + error=true; + error_reported.set(5); + }else{ + error_reported.reset(5); + } + + // if(error) { + // stream->printf("%08X\n", driver_status_result); + // } + return error; +} + +bool TMC21X::checkAlarm() //TODO Convert +{ + return check_error_status_bits(THEKERNEL->streams); +} + +// sets a raw register to the value specified, for advanced settings +// register 255 writes them, 0 displays what registers are mapped to what +// FIXME status registers not reading back correctly, check docs +bool TMC21X::setRawRegister(StreamOutput *stream, uint32_t reg, uint32_t val) //TODO Convert +{ + switch(reg) { + case 255: + send2130_old(driver_control_register_value); + send2130_old(chopper_config_register); + send2130_old(cool_step_register_value); + send2130_old(stall_guard2_current_register_value); + send2130_old(driver_configuration_register_value); + stream->printf("Registers written\n"); + break; + + + case 1: driver_control_register_value = val; stream->printf("driver control register set to %08lX\n", val); break; + case 2: chopper_config_register = val; stream->printf("chopper config register set to %08lX\n", val); break; + case 3: cool_step_register_value = val; stream->printf("cool step register set to %08lX\n", val); break; + case 4: stall_guard2_current_register_value = val; stream->printf("stall guard2 current register set to %08lX\n", val); break; + case 5: driver_configuration_register_value = val; stream->printf("driver configuration register set to %08lX\n", val); break; + + default: + stream->printf("1: driver control register\n"); + stream->printf("2: chopper config register\n"); + stream->printf("3: cool step register\n"); + stream->printf("4: stall guard2 current register\n"); + stream->printf("5: driver configuration register\n"); + stream->printf("255: update all registers\n"); + return false; + } + return true; +} + +/* + * send register settings to the stepper driver via SPI + * returns the current status + * sends 20bits, the last 20 bits of the 24bits is taken as the command + */ +void TMC21X::send2130_old(unsigned long datagram) //TODO Convert +{ + return; //TODO remove this routine + + uint8_t buf[] {(uint8_t)(datagram >> 16), (uint8_t)(datagram >> 8), (uint8_t)(datagram & 0xff)}; + uint8_t rbuf[3]; + + //write/read the values + spi(buf, 3, rbuf); + + // construct reply + unsigned long i_datagram = ((rbuf[0] << 16) | (rbuf[1] << 8) | (rbuf[2])) >> 4; + + //store the datagram as status result + driver_status_result = i_datagram; + + //THEKERNEL->streams->printf("sent: %02X, %02X, %02X received: %02X, %02X, %02X \n", buf[0], buf[1], buf[2], rbuf[0], rbuf[1], rbuf[2]); +} + +/* + * send register settings to the stepper driver via SPI + * returns the current status 40 bit datagram, first 8 bits is the status, the last 32 bits are the register contents + */ +void TMC21X::send2130(uint8_t reg, uint32_t datagram) //TODO Converted, needs testing +{ + uint8_t buf[5]; + + //Note: SPI write frist to last //TODO check that this is actually working as may have just swapped bytes and not the bit order + buf[0] = (uint8_t)(reg); + buf[1] = (uint8_t)(datagram >> 24); + buf[2] = (uint8_t)(datagram >> 16); + buf[3] = (uint8_t)(datagram >> 8); + buf[4] = (uint8_t)(datagram >> 0); + + uint8_t rbuf[5]; + + //write/read the values + spi(buf, 5, rbuf); + + // construct reply + uint32_t i_datagram = ((uint32_t)(rbuf[3] << 24) | (uint32_t)(rbuf[2] <<16) | (uint32_t)(rbuf[1] <<8) | (uint32_t)(rbuf[0] >>24) ); + + //store the datagram as status result + driver_status_result = i_datagram; + driver_status = rbuf[4]; //save the status register + + //THEKERNEL->streams->printf("sent: %02X, %02X, %02X, %02X, %02X received: %02X, %02X, %02X, %02X, %02X \n", buf[4], buf[4], buf[2], buf[1], buf[0], rbuf[4], rbuf[3], rbuf[2], rbuf[1], rbuf[0]); +} + + +#define HAS(X) (options.find(X) != options.end()) +#define GET(X) (options.at(X)) +bool TMC21X::set_options(const options_t& options) //TODO Convert +{ + bool set = false; + if(HAS('O') || HAS('Q')) { + // void TMC21X::setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled) + int8_t o = HAS('O') ? GET('O') : getStallGuardThreshold(); + int8_t q = HAS('Q') ? GET('Q') : getStallGuardFilter(); + setStallGuardThreshold(o, q); + set = true; + } + + if(HAS('H') && HAS('I') && HAS('J') && HAS('K') && HAS('L')) { + //void TMC21X::setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size, uint8_t current_increment_step_size, uint8_t lower_current_limit) + setCoolStepConfiguration(GET('H'), GET('I'), GET('J'), GET('K'), GET('L')); + set = true; + } + + if(HAS('S')) { + uint32_t s = GET('S'); + if(s == 0 && HAS('U') && HAS('V') && HAS('W') && HAS('X') && HAS('Y')) { + //void TMC21X::setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator) + setConstantOffTimeChopper(GET('U'), GET('V'), GET('W'), GET('X'), GET('Y')); + set = true; + + } else if(s == 1 && HAS('U') && HAS('V') && HAS('W') && HAS('X') && HAS('Y')) { + //void TMC21X::setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement); + setSpreadCycleChopper(GET('U'), GET('V'), GET('W'), GET('X'), GET('Y')); + set = true; + + } else if(s == 2 && HAS('Z')) { + setRandomOffTime(GET('Z')); + set = true; + + } else if(s == 3 && HAS('Z')) { + setDoubleEdge(GET('Z')); + set = true; + + } else if(s == 4 && HAS('Z')) { + setStepInterpolation(GET('Z')); + set = true; + + } else if(s == 5 && HAS('Z')) { + setCoolStepEnabled(GET('Z') == 1); + set = true; + } + } + + return set; +} diff --git a/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.h b/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.h new file mode 100644 index 00000000..e7ceb6e1 --- /dev/null +++ b/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.h @@ -0,0 +1,455 @@ +/* + modified from... + TMC21X.cpp - - TMC21X Stepper library for Wiring/Arduino + + based on the stepper library by Tom Igoe, et. al. + + Copyright (c) 2011, Interactive Matter, Marcus Nowotny + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + */ + + +// ensure this library description is only included once +#pragma once + +#include +#include +#include + +class StreamOutput; + +/*! + * \class TMC21X + * \brief Class representing a TMC21X stepper driver + */ +#define TMC2130_max_current 31 //TMC2130 only allows a ratio of 0..31 which represents 1/32 .. 32/32 + +class TMC21X +{ +public: + /*! + * \brief creates a new representation of a stepper motor connected to a TMC21X stepper driver + * + * This is the main constructor. If in doubt use this. You must provide all parameters as described below. + * + * \param spi send function + * + * By default the Constant Off Time chopper is used, see TCM21X.setConstantOffTimeChopper() for details. + * This should work on most motors (YMMV). You may want to configure and use the Spread Cycle Chopper, see setSpreadCycleChopper(). + * + * By default a microstepping of 1/32th is used to provide a smooth motor run, while still giving a good progression per step. + * You can select a different stepping with setMicrosteps() to aa different value. + * \sa start(), setMicrosteps() + */ + TMC21X(std::function spi, char designator); +// + /*! + * \brief configures the TMC21X stepper driver. Before you called this function the stepper driver is in nonfunctional mode. + * + * \param rms_current the maximum current to provide to the motor in mA (!). A value of 200 would send up to 200mA to the motor + * \param resistor the current sense resistor in milli Ohm, defaults to ,15 Ohm ( or 150 milli Ohm) as in the TMC260 Arduino Shield + + * This routine configures the TMC21X stepper driver for the given values via SPI. + * Most member functions are non functional if the driver has not been started. + * Therefore it is best to call this in your Arduino setup() function. + */ + void init(uint16_t cs); + + /*! + * \brief Set the number of microsteps in 2^i values (rounded) up to 256 + * + * This method set's the number of microsteps per step in 2^i interval. + * This means you can select 1, 2, 4, 16, 32, 64, 128 or 256 as valid microsteps. + * If you give any other value it will be rounded to the next smaller number (3 would give a microstepping of 2). + * You can always check the current microstepping with getMicrosteps(). + */ + void setMicrosteps(int number_of_steps); + + /*! + * \brief returns the effective current number of microsteps selected. + * + * This function always returns the effective number of microsteps. + * This can be a bit different than the micro steps set in setMicrosteps() since it is rounded to 2^i. + * + * \sa setMicrosteps() + */ + int getMicrosteps(void); + + void setStepInterpolation(int8_t value); + void setDoubleEdge(int8_t value); + + /*! + * \brief Sets and configure the classical Constant Off Timer Chopper + * \param constant_off_time The off time setting controls the minimum chopper frequency. For most applications an off time within the range of 5μs to 20μs will fit. Setting this parameter to zero completely disables all driver transistors and the motor can free-wheel. 0: chopper off, 1:15: off time setting (1 will work with minimum blank time of 24 clocks) + * \param blank_time Selects the comparator blank time. This time needs to safely cover the switching event and the duration of the ringing on the sense resistor. For most low current drivers, a setting of 1 or 2 is good. For high current applications with large MOSFETs, a setting of 2 or 3 will be required. 0 (min setting) … (3) amx setting + * \param fast_decay_time_setting Fast decay time setting. Controls the portion of fast decay for each chopper cycle. 0: slow decay only, 1…15: duration of fast decay phase + * \param sine_wave_offset Sine wave offset. Controls the sine wave offset. A positive offset corrects for zero crossing error. -3…-1: negative offset, 0: no offset,1…12: positive offset + * \param use_curreent_comparator Selects usage of the current comparator for termination of the fast decay cycle. If current comparator is enabled, it terminates the fast decay cycle in case the current reaches a higher negative value than the actual positive value. (0 disable, -1 enable). + * + * The classic constant off time chopper uses a fixed portion of fast decay following each on phase. + * While the duration of the on time is determined by the chopper comparator, the fast decay time needs + * to be set by the user in a way, that the current decay is enough for the driver to be able to follow + * the falling slope of the sine wave, and on the other hand it should not be too long, in order to minimize + * motor current ripple and power dissipation. This best can be tuned using an oscilloscope or + * trying out motor smoothness at different velocities. A good starting value is a fast decay time setting + * similar to the slow decay time setting. + * After tuning of the fast decay time, the offset should be determined, in order to have a smooth zero transition. + * This is necessary, because the fast decay phase leads to the absolute value of the motor current being lower + * than the target current (see figure 17). If the zero offset is too low, the motor stands still for a short + * moment during current zero crossing, if it is set too high, it makes a larger microstep. + * Typically, a positive offset setting is required for optimum operation. + * + * \sa setSpreadCycleChoper() for other alternatives. + * \sa setRandomOffTime() for spreading the noise over a wider spectrum + */ + void setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator); + + /*! + * \brief Sets and configures with spread cycle chopper. + * \param constant_off_time The off time setting controls the minimum chopper frequency. For most applications an off time within the range of 5μs to 20μs will fit. Setting this parameter to zero completely disables all driver transistors and the motor can free-wheel. 0: chopper off, 1:15: off time setting (1 will work with minimum blank time of 24 clocks) + * \param blank_time Selects the comparator blank time. This time needs to safely cover the switching event and the duration of the ringing on the sense resistor. For most low current drivers, a setting of 1 or 2 is good. For high current applications with large MOSFETs, a setting of 2 or 3 will be required. 0 (min setting) … (3) amx setting + * \param hysteresis_start Hysteresis start setting. Please remark, that this value is an offset to the hysteresis end value. 1 … 8 + * \param hysteresis_end Hysteresis end setting. Sets the hysteresis end value after a number of decrements. Decrement interval time is controlled by hysteresis_decrement. The sum hysteresis_start + hysteresis_end must be <16. At a current setting CS of max. 30 (amplitude reduced to 240), the sum is not limited. + * \param hysteresis_decrement Hysteresis decrement setting. This setting determines the slope of the hysteresis during on time and during fast decay time. 0 (fast decrement) … 3 (slow decrement). + * + * The spreadCycle chopper scheme (pat.fil.) is a precise and simple to use chopper principle, which automatically determines + * the optimum fast decay portion for the motor. Anyhow, a number of settings can be made in order to optimally fit the driver + * to the motor. + * Each chopper cycle is comprised of an on-phase, a slow decay phase, a fast decay phase and a second slow decay phase. + * The slow decay phases limit the maximum chopper frequency and are important for low motor and driver power dissipation. + * The hysteresis start setting limits the chopper frequency by forcing the driver to introduce a minimum amount of + * current ripple into the motor coils. The motor inductivity determines the ability to follow a changing motor current. + * The duration of the on- and fast decay phase needs to cover at least the blank time, because the current comparator is + * disabled during this time. + * + * \sa setRandomOffTime() for spreading the noise over a wider spectrum + */ + void setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement); + + /*! + * \brief Use random off time for noise reduction (0 for off, -1 for on). + * \param value 0 for off, -1 for on + * + * In a constant off time chopper scheme both coil choppers run freely, i.e. are not synchronized. + * The frequency of each chopper mainly depends on the coil current and the position dependant motor coil inductivity, + * thus it depends on the microstep position. With some motors a slightly audible beat can occur between the chopper + * frequencies, especially when they are near to each other. This typically occurs at a few microstep positions within + * each quarter wave. + * This effect normally is not audible when compared to mechanical noise generated by ball bearings, + * etc. Further factors which can cause a similar effect are a poor layout of sense resistor GND connection. + * In order to minimize the effect of a beat between both chopper frequencies, an internal random generator is provided. + * It modulates the slow decay time setting when switched on. The random off time feature further spreads the chopper spectrum, + * reducing electromagnetic emission on single frequencies. + */ + void setRandomOffTime(int8_t value); + + /*! + * \brief set the maximum motor current in mA (1000 is 1 Amp) + * Keep in mind this is the maximum peak Current. The RMS current will be 1/sqrt(2) smaller. The actual current can also be smaller + * by employing CoolStep. + * \param current the maximum motor current in mA + * \sa getCurrent(), getCurrentCurrent() + */ + void setCurrent(unsigned int current); + + /*! + * \brief readout the motor maximum current in mA (1000 is an Amp) + * This is the maximum current. to get the current current - which may be affected by CoolStep us getCurrentCurrent() + *\return the maximum motor current in milli amps + * \sa getCurrentCurrent() + */ + unsigned int getCurrent(void); + + /*! + * \brief set the StallGuard threshold in order to get sensible StallGuard readings. + * \param stall_guard_threshold -64 … 63 the StallGuard threshold + * \param stall_guard_filter_enabled 0 if the filter is disabled, -1 if it is enabled + * + * The StallGuard threshold is used to optimize the StallGuard reading to sensible values. It should be at 0 at + * the maximum allowable load on the motor (but not before). = is a good starting point (and the default) + * If you get Stall Gaurd readings of 0 without any load or with too little load increase the value. + * If you get readings of 1023 even with load decrease the setting. + * + * If you switch on the filter the StallGuard reading is only updated each 4th full step to reduce the noise in the + * reading. + * + * \sa getCurrentStallGuardReading() to read out the current value. + */ + void setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled); + + /*! + * \brief reads out the StallGuard threshold + * \return a number between -64 and 63. + */ + int8_t getStallGuardThreshold(void); + + /*! + * \brief returns the current setting of the StallGuard filter + * \return 0 if not set, -1 if set + */ + int8_t getStallGuardFilter(void); + + /*! + * \brief This method configures the CoolStep smart energy operation. You must have a proper StallGuard configuration for the motor situation (current, voltage, speed) in rder to use this feature. + * \param lower_SG_threshold Sets the lower threshold for stallGuard2TM reading. Below this value, the motor current becomes increased. Allowed values are 0...480 + * \param SG_hysteresis Sets the distance between the lower and the upper threshold for stallGuard2TM reading. Above the upper threshold (which is lower_SG_threshold+SG_hysteresis+1) the motor current becomes decreased. Allowed values are 0...480 + * \param current_decrement_step_size Sets the current decrement steps. If the StallGuard value is above the threshold the current gets decremented by this step size. 0...32 + * \param current_increment_step_size Sets the current increment step. The current becomes incremented for each measured stallGuard2TM value below the lower threshold. 0...8 + * \param lower_current_limit Sets the lower motor current limit for coolStepTM operation by scaling the CS value. Values can be COOL_STEP_HALF_CS_LIMIT, COOL_STEP_QUARTER_CS_LIMIT + * The CoolStep smart energy operation automatically adjust the current sent into the motor according to the current load, + * read out by the StallGuard in order to provide the optimum torque with the minimal current consumption. + * You configure the CoolStep current regulator by defining upper and lower bounds of StallGuard readouts. If the readout is above the + * limit the current gets increased, below the limit the current gets decreased. + * You can specify the upper an lower threshold of the StallGuard readout in order to adjust the current. You can also set the number of + * StallGuard readings necessary above or below the limit to get a more stable current adjustment. + * The current adjustment itself is configured by the number of steps the current gets in- or decreased and the absolute minimum current + * (1/2 or 1/4th of the configured current). + * \sa COOL_STEP_HALF_CS_LIMIT, COOL_STEP_QUARTER_CS_LIMIT + */ + void setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size, + uint8_t current_increment_step_size, uint8_t lower_current_limit); + + /*! + * \brief enables or disables the CoolStep smart energy operation feature. It must be configured before enabling it. + * \param enabled true if CoolStep should be enabled, false if not. + * \sa setCoolStepConfiguration() + */ + void setCoolStepEnabled(bool enabled); + + + /*! + * \brief check if the CoolStep feature is enabled + * \sa setCoolStepEnabled() + */ + bool isCoolStepEnabled(); + + /*! + * \brief returns the lower StallGuard threshold for the CoolStep operation + * \sa setCoolStepConfiguration() + */ + unsigned int getCoolStepLowerSgThreshold(); + + /*! + * \brief returns the upper StallGuard threshold for the CoolStep operation + * \sa setCoolStepConfiguration() + */ + unsigned int getCoolStepUpperSgThreshold(); + + /*! + * \brief returns the number of StallGuard readings befor CoolStep adjusts the motor current. + * \sa setCoolStepConfiguration() + */ + uint8_t getCoolStepNumberOfSGReadings(); + + /*! + * \brief returns the increment steps for the current for the CoolStep operation + * \sa setCoolStepConfiguration() + */ + uint8_t getCoolStepCurrentIncrementSize(); + + /*! + * \brief returns the absolute minimum current for the CoolStep operation + * \sa setCoolStepConfiguration() + * \sa COOL_STEP_HALF_CS_LIMIT, COOL_STEP_QUARTER_CS_LIMIT + */ + uint8_t getCoolStepLowerCurrentLimit(); + + /*! + * \brief Reads the current StallGuard value. + * \return The current StallGuard value, lesser values indicate higher load, 0 means stall detected. + * Keep in mind that this routine reads and writes a value via SPI - so this may take a bit time. + * \sa setStallGuardThreshold() for tuning the readout to sensible ranges. + */ + int getCurrentStallGuardReading(void); + + /*! + * \brief Reads the current current setting value as fraction of the maximum current + * Returns values between 0 and 31, representing 1/32 to 32/32 (=1) + * \sa setCoolStepConfiguration() + */ + uint8_t getCurrentCSReading(void); + + + /*! + *\brief a convenience method to determine if the current scaling uses 0.31V or 0.165V as reference. + *\return false if 0.13V is the reference voltage, true if 0.165V is used. + */ + bool isCurrentScalingHalfed(); + + /*! + * \brief Reads the current current setting value and recalculates the absolute current in mA (1A would be 1000). + * This method calculates the currently used current setting (either by setting or by CoolStep) and reconstructs + * the current in mA by using the VSENSE and resistor value. This method uses floating point math - so it + * may not be the fastest. + * \sa getCurrentCSReading(), getResistor(), isCurrentScalingHalfed(), getCurrent() + */ + unsigned int getCoolstepCurrent(void); + + /*! + * \brief checks if there is a StallGuard warning in the last status + * \return 0 if there was no warning, -1 if there was some warning. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + * + * \sa setStallGuardThreshold() for tuning the readout to sensible ranges. + */ + bool isStallGuardOverThreshold(void); + + /*! + * \brief Return over temperature status of the last status readout + * return 0 is everything is OK, TMC21X_OVERTEMPERATURE_PREWARING if status is reached, TMC21X_OVERTEMPERATURE_SHUTDOWN is the chip is shutdown, -1 if the status is unknown. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + int8_t getOverTemperature(void); + + /*! + * \brief Is motor channel A shorted to ground detected in the last status readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + + bool isShortToGroundA(void); + + /*! + * \brief Is motor channel B shorted to ground detected in the last status readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isShortToGroundB(void); + /*! + * \brief iIs motor channel A connected according to the last status readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isOpenLoadA(void); + + /*! + * \brief iIs motor channel A connected according to the last status readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isOpenLoadB(void); + + /*! + * \brief Is chopper inactive since 2^20 clock cycles - defaults to ~0,08s + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isStandStill(void); + + /*! + * \brief checks if there is a StallGuard warning in the last status + * \return 0 if there was no warning, -1 if there was some warning. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + * + * \sa isStallGuardOverThreshold() + * TODO why? + * + * \sa setStallGuardThreshold() for tuning the readout to sensible ranges. + */ + bool isStallGuardReached(void); + + /*! + *\brief enables or disables the motor driver bridges. If disabled the motor can run freely. If enabled not. + *\param enabled a bool value true if the motor should be enabled, false otherwise. + */ + void setEnabled(bool enabled); + + /*! + *\brief checks if the output bridges are enabled. If the bridges are not enabled the motor can run freely + *\return true if the bridges and by that the motor driver are enabled, false if not. + *\sa setEnabled() + */ + bool isEnabled(); + + /*! + * \brief Manually read out the status register + * This function sends a byte to the motor driver in order to get the current readout. The parameter read_value + * selects which value will get returned. If the read_vlaue changes in respect to the previous readout this method + * automatically send two bytes to the motor: one to set the readout and one to get the actual readout. So this method + * may take time to send and read one or two bits - depending on the previous readout. + * \param read_value selects which value to read out (0..3). You can use the defines TMC21X_READOUT_POSITION, TMC_2130_READOUT_STALLGUARD, or TMC_2130_READOUT_CURRENT + * \sa TMC21X_READOUT_POSITION, TMC_2130_READOUT_STALLGUARD, TMC_2130_READOUT_CURRENT + */ + void readStatus(int8_t read_value); + + /*! + * \brief Prints out all the information that can be found in the last status read out - it does not force a status readout. + * The result is printed via Serial + */ + void dumpStatus(StreamOutput *stream, bool readable= true); + bool setRawRegister(StreamOutput *stream, uint32_t reg, uint32_t val); + bool checkAlarm(); + + using options_t= std::map; + + bool set_options(const options_t& options); + +private: + //helper routine to get the top 10 bit of the readout + inline int getReadoutValue(); + bool check_error_status_bits(StreamOutput *stream); + + // SPI sender + inline void send2130_old(unsigned long datagram); + inline void send2130(uint8_t reg, uint32_t datagram); + std::function spi; + + unsigned int resistor{50}; // current sense resistor value in milliohm + + //driver control register copies to easily set & modify the registers + unsigned long driver_control_register_value; + unsigned long chopper_config_register; + unsigned long cool_step_register_value; + unsigned long stall_guard2_current_register_value; + unsigned long driver_configuration_register_value; + //the driver status result + uint32_t driver_status_result; + uint8_t driver_status; + + //status values + int microsteps; //the current number of micro steps + + std::bitset<8> error_reported; + + // only needed for the tuning app report + struct { + int8_t blank_time:8; + int8_t constant_off_time:5; //we need to remember this value in order to enable and disable the motor + int8_t h_start:4; + int8_t h_end:4; + int8_t h_decrement:3; + bool cool_step_enabled:1; //we need to remember this to configure the coolstep if it is enabled + bool started:1; //if the stepper has been started yet + }; + + uint8_t cool_step_lower_threshold; // we need to remember the threshold to enable and disable the CoolStep feature + char designator; + +}; + diff --git a/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp new file mode 100644 index 00000000..d9a52224 --- /dev/null +++ b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp @@ -0,0 +1,1133 @@ +/* + Highly modifed from.... + + TMC26X.cpp - - TMC26X Stepper library for Wiring/Arduino + + based on the stepper library by Tom Igoe, et. al. + + Copyright (c) 2011, Interactive Matter, Marcus Nowotny + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + */ + +#include "TMC26X.h" +#include "mbed.h" +#include "StreamOutput.h" +#include "Kernel.h" +#include "libs/StreamOutputPool.h" +#include "Robot.h" +#include "StepperMotor.h" +#include "ConfigValue.h" +#include "Config.h" +#include "checksumm.h" + + +#define motor_driver_control_checksum CHECKSUM("motor_driver_control") +#define sense_resistor_checksum CHECKSUM("sense_resistor") + +//! return value for TMC26X.getOverTemperature() if there is a overtemperature situation in the TMC chip +/*! + * This warning indicates that the TCM chip is too warm. + * It is still working but some parameters may be inferior. + * You should do something against it. + */ +#define TMC26X_OVERTEMPERATURE_PREWARING 1 +//! return value for TMC26X.getOverTemperature() if there is a overtemperature shutdown in the TMC chip +/*! + * This warning indicates that the TCM chip is too warm to operate and has shut down to prevent damage. + * It will stop working until it cools down again. + * If you encouter this situation you must do something against it. Like reducing the current or improving the PCB layout + * and/or heat management. + */ +#define TMC26X_OVERTEMPERATURE_SHUTDOWN 2 + +//which values can be read out +/*! + * Selects to readout the microstep position from the motor. + *\sa readStatus() + */ +#define TMC26X_READOUT_POSITION 0 +/*! + * Selects to read out the StallGuard value of the motor. + *\sa readStatus() + */ +#define TMC26X_READOUT_STALLGUARD 1 +/*! + * Selects to read out the current current setting (acc. to CoolStep) and the upper bits of the StallGuard value from the motor. + *\sa readStatus(), setCurrent() + */ +#define TMC26X_READOUT_CURRENT 3 + +/*! + * Define to set the minimum current for CoolStep operation to 1/2 of the selected CS minium. + *\sa setCoolStepConfiguration() + */ +#define COOL_STEP_HALF_CS_LIMIT 0 +/*! + * Define to set the minimum current for CoolStep operation to 1/4 of the selected CS minium. + *\sa setCoolStepConfiguration() + */ +#define COOL_STEP_QUARTDER_CS_LIMIT 1 + + +//some default values used in initialization +#define DEFAULT_MICROSTEPPING_VALUE 32 + +//TMC26X register definitions +#define DRIVER_CONTROL_REGISTER 0x00000ul +#define CHOPPER_CONFIG_REGISTER 0x80000ul +#define COOL_STEP_REGISTER 0xA0000ul +#define STALL_GUARD2_LOAD_MEASURE_REGISTER 0xC0000ul +#define DRIVER_CONFIG_REGISTER 0xE0000ul + +#define REGISTER_BIT_PATTERN 0xFFFFFul + +//definitions for the driver control register DRVCTL +#define MICROSTEPPING_PATTERN 0x000Ful +#define STEP_INTERPOLATION 0x0200ul +#define DOUBLE_EDGE_STEP 0x0100ul + +//definitions for the driver config register DRVCONF +#define READ_MICROSTEP_POSITION 0x0000ul +#define READ_STALL_GUARD_READING 0x0010ul +#define READ_STALL_GUARD_AND_COOL_STEP 0x0020ul +#define READ_SELECTION_PATTERN 0x0030ul +#define VSENSE 0x0040ul + +//definitions for the chopper config register +#define CHOPPER_MODE_STANDARD 0x00000ul +#define CHOPPER_MODE_T_OFF_FAST_DECAY 0x04000ul +#define T_OFF_PATTERN 0x0000ful +#define RANDOM_TOFF_TIME 0x02000ul +#define BLANK_TIMING_PATTERN 0x18000ul +#define BLANK_TIMING_SHIFT 15 +#define HYSTERESIS_DECREMENT_PATTERN 0x01800ul +#define HYSTERESIS_DECREMENT_SHIFT 11 +#define HYSTERESIS_LOW_VALUE_PATTERN 0x00780ul +#define HYSTERESIS_LOW_SHIFT 7 +#define HYSTERESIS_START_VALUE_PATTERN 0x00078ul +#define HYSTERESIS_START_VALUE_SHIFT 4 +#define T_OFF_TIMING_PATERN 0x0000Ful + +//definitions for cool step register +#define MINIMUM_CURRENT_FOURTH 0x8000ul +#define CURRENT_DOWN_STEP_SPEED_PATTERN 0x6000ul +#define SE_MAX_PATTERN 0x0F00ul +#define SE_CURRENT_STEP_WIDTH_PATTERN 0x0060ul +#define SE_MIN_PATTERN 0x000Ful + +//definitions for stall guard2 current register +#define STALL_GUARD_FILTER_ENABLED 0x10000ul +#define STALL_GUARD_TRESHHOLD_VALUE_PATTERN 0x17F00ul +#define CURRENT_SCALING_PATTERN 0x0001Ful +#define STALL_GUARD_CONFIG_PATTERN 0x17F00ul +#define STALL_GUARD_VALUE_PATTERN 0x07F00ul + +//definitions for the input from the TCM2600 +#define STATUS_STALL_GUARD_STATUS 0x00001ul +#define STATUS_OVER_TEMPERATURE_SHUTDOWN 0x00002ul +#define STATUS_OVER_TEMPERATURE_WARNING 0x00004ul +#define STATUS_SHORT_TO_GROUND_A 0x00008ul +#define STATUS_SHORT_TO_GROUND_B 0x00010ul +#define STATUS_OPEN_LOAD_A 0x00020ul +#define STATUS_OPEN_LOAD_B 0x00040ul +#define STATUS_STAND_STILL 0x00080ul +#define READOUT_VALUE_PATTERN 0xFFC00ul + +//debuging output +//#define DEBUG + +/* + * Constructor + */ +TMC26X::TMC26X(std::function spi, char d) : spi(spi), designator(d) +{ + //we are not started yet + started = false; + //by default cool step is not enabled + cool_step_enabled = false; + error_reported.reset(); +} + +/* + * configure the stepper driver + * just must be called. + */ +void TMC26X::init(uint16_t cs) +{ + // read chip specific config entries + this->resistor= THEKERNEL->config->value(motor_driver_control_checksum, cs, sense_resistor_checksum)->by_default(50)->as_number(); // in milliohms + + //setting the default register values + driver_control_register_value = DRIVER_CONTROL_REGISTER; + chopper_config_register = CHOPPER_CONFIG_REGISTER; + cool_step_register_value = COOL_STEP_REGISTER; + stall_guard2_current_register_value = STALL_GUARD2_LOAD_MEASURE_REGISTER; + driver_configuration_register_value = DRIVER_CONFIG_REGISTER | READ_STALL_GUARD_READING; + + //set the initial values + send262(driver_control_register_value); + send262(chopper_config_register); + send262(cool_step_register_value); + send262(stall_guard2_current_register_value); + send262(driver_configuration_register_value); + + started = true; + +#if 1 + //set to a conservative start value + setConstantOffTimeChopper(7, 54, 13, 12, 1); +#else + // for 1.5amp kysan @ 12v + setSpreadCycleChopper(5, 54, 5, 0, 0); + // for 4amp Nema24 @ 12v + //setSpreadCycleChopper(5, 54, 4, 0, 0); +#endif + + setEnabled(false); + + //set a nice microstepping value + setMicrosteps(DEFAULT_MICROSTEPPING_VALUE); + + // set stallguard to a conservative value so it doesn't trigger immediately + setStallGuardThreshold(10, 1); +} + +void TMC26X::setCurrent(unsigned int current) +{ + uint8_t current_scaling = 0; + //calculate the current scaling from the max current setting (in mA) + double mASetting = (double)current; + double resistor_value = (double) this->resistor; + // remove vesense flag + this->driver_configuration_register_value &= ~(VSENSE); + //this is derrived from I=(cs+1)/32*(Vsense/Rsense) + //leading to cs = CS = 32*R*I/V (with V = 0,31V oder 0,165V and I = 1000*current) + //with Rsense=0,15 + //for vsense = 0,310V (VSENSE not set) + //or vsense = 0,165V (VSENSE set) + current_scaling = (uint8_t)((resistor_value * mASetting * 32.0F / (0.31F * 1000.0F * 1000.0F)) - 0.5F); //theoretically - 1.0 for better rounding it is 0.5 + + //check if the current scaling is too low + if (current_scaling < 16) { + //set the csense bit to get a use half the sense voltage (to support lower motor currents) + this->driver_configuration_register_value |= VSENSE; + //and recalculate the current setting + current_scaling = (uint8_t)((resistor_value * mASetting * 32.0F / (0.165F * 1000.0F * 1000.0F)) - 0.5F); //theoretically - 1.0 for better rounding it is 0.5 + } + + //do some sanity checks + if (current_scaling > 31) { + current_scaling = 31; + } + //delete the old value + stall_guard2_current_register_value &= ~(CURRENT_SCALING_PATTERN); + //set the new current scaling + stall_guard2_current_register_value |= current_scaling; + //if started we directly send it to the motor + if (started) { + send262(driver_configuration_register_value); + send262(stall_guard2_current_register_value); + } +} + +unsigned int TMC26X::getCurrent(void) +{ + //we calculate the current according to the datasheet to be on the safe side + //this is not the fastest but the most accurate and illustrative way + double result = (double)(stall_guard2_current_register_value & CURRENT_SCALING_PATTERN); + double resistor_value = (double)this->resistor; + double voltage = (driver_configuration_register_value & VSENSE) ? 0.165F : 0.31F; + result = (result + 1.0F) / 32.0F * voltage / resistor_value * 1000.0F * 1000.0F; + return (unsigned int)result; +} + +void TMC26X::setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled) +{ + if (stall_guard_threshold < -64) { + stall_guard_threshold = -64; + //We just have 5 bits + } else if (stall_guard_threshold > 63) { + stall_guard_threshold = 63; + } + //add trim down to 7 bits + stall_guard_threshold &= 0x7f; + //delete old stall guard settings + stall_guard2_current_register_value &= ~(STALL_GUARD_CONFIG_PATTERN); + if (stall_guard_filter_enabled) { + stall_guard2_current_register_value |= STALL_GUARD_FILTER_ENABLED; + } + //Set the new stall guard threshold + stall_guard2_current_register_value |= (((unsigned long)stall_guard_threshold << 8) & STALL_GUARD_CONFIG_PATTERN); + //if started we directly send it to the motor + if (started) { + send262(stall_guard2_current_register_value); + } +} + +int8_t TMC26X::getStallGuardThreshold(void) +{ + unsigned long stall_guard_threshold = stall_guard2_current_register_value & STALL_GUARD_VALUE_PATTERN; + //shift it down to bit 0 + stall_guard_threshold >>= 8; + //convert the value to an int to correctly handle the negative numbers + int8_t result = stall_guard_threshold; + //check if it is negative and fill it up with leading 1 for proper negative number representation + if (result & (1 << 6)) { + result |= 0xC0; + } + return result; +} + +int8_t TMC26X::getStallGuardFilter(void) +{ + if (stall_guard2_current_register_value & STALL_GUARD_FILTER_ENABLED) { + return -1; + } else { + return 0; + } +} +/* + * Set the number of microsteps per step. + * 0,2,4,8,16,32,64,128,256 is supported + * any value in between will be mapped to the next smaller value + * 0 and 1 set the motor in full step mode + */ +void TMC26X::setMicrosteps(int number_of_steps) +{ + long setting_pattern; + //poor mans log + if (number_of_steps >= 256) { + setting_pattern = 0; + microsteps = 256; + } else if (number_of_steps >= 128) { + setting_pattern = 1; + microsteps = 128; + } else if (number_of_steps >= 64) { + setting_pattern = 2; + microsteps = 64; + } else if (number_of_steps >= 32) { + setting_pattern = 3; + microsteps = 32; + } else if (number_of_steps >= 16) { + setting_pattern = 4; + microsteps = 16; + } else if (number_of_steps >= 8) { + setting_pattern = 5; + microsteps = 8; + } else if (number_of_steps >= 4) { + setting_pattern = 6; + microsteps = 4; + } else if (number_of_steps >= 2) { + setting_pattern = 7; + microsteps = 2; + //1 and 0 lead to full step + } else if (number_of_steps <= 1) { + setting_pattern = 8; + microsteps = 1; + } + + //delete the old value + this->driver_control_register_value &= 0xFFFF0ul; + //set the new value + this->driver_control_register_value |= setting_pattern; + + //if started we directly send it to the motor + if (started) { + send262(driver_control_register_value); + } +} + +/* + * returns the effective number of microsteps at the moment + */ +int TMC26X::getMicrosteps(void) +{ + return microsteps; +} + +void TMC26X::setStepInterpolation(int8_t value) +{ + if (value) { + driver_control_register_value |= STEP_INTERPOLATION; + } else { + driver_control_register_value &= ~(STEP_INTERPOLATION); + } + //if started we directly send it to the motor + if (started) { + send262(driver_control_register_value); + } +} + +void TMC26X::setDoubleEdge(int8_t value) +{ + if (value) { + driver_control_register_value |= DOUBLE_EDGE_STEP; + } else { + driver_control_register_value &= ~(DOUBLE_EDGE_STEP); + } + //if started we directly send it to the motor + if (started) { + send262(driver_control_register_value); + } +} + +/* + * constant_off_time: The off time setting controls the minimum chopper frequency. + * For most applications an off time within the range of 5μs to 20μs will fit. + * 2...15: off time setting + * + * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the + * duration of the ringing on the sense resistor. For + * 0: min. setting 3: max. setting + * + * fast_decay_time_setting: Fast decay time setting. With CHM=1, these bits control the portion of fast decay for each chopper cycle. + * 0: slow decay only + * 1...15: duration of fast decay phase + * + * sine_wave_offset: Sine wave offset. With CHM=1, these bits control the sine wave offset. + * A positive offset corrects for zero crossing error. + * -3..-1: negative offset 0: no offset 1...12: positive offset + * + * use_current_comparator: Selects usage of the current comparator for termination of the fast decay cycle. + * If current comparator is enabled, it terminates the fast decay cycle in case the current + * reaches a higher negative value than the actual positive value. + * 1: enable comparator termination of fast decay cycle + * 0: end by time only + */ +void TMC26X::setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator) +{ + //perform some sanity checks + if (constant_off_time < 2) { + constant_off_time = 2; + } else if (constant_off_time > 15) { + constant_off_time = 15; + } + //save the constant off time + this->constant_off_time = constant_off_time; + int8_t blank_value; + //calculate the value acc to the clock cycles + if (blank_time >= 54) { + blank_value = 3; + } else if (blank_time >= 36) { + blank_value = 2; + } else if (blank_time >= 24) { + blank_value = 1; + } else { + blank_value = 0; + } + this->blank_time = blank_time; + + if (fast_decay_time_setting < 0) { + fast_decay_time_setting = 0; + } else if (fast_decay_time_setting > 15) { + fast_decay_time_setting = 15; + } + if (sine_wave_offset < -3) { + sine_wave_offset = -3; + } else if (sine_wave_offset > 12) { + sine_wave_offset = 12; + } + //shift the sine_wave_offset + sine_wave_offset += 3; + + //calculate the register setting + //first of all delete all the values for this + chopper_config_register &= ~((1 << 12) | BLANK_TIMING_PATTERN | HYSTERESIS_DECREMENT_PATTERN | HYSTERESIS_LOW_VALUE_PATTERN | HYSTERESIS_START_VALUE_PATTERN | T_OFF_TIMING_PATERN); + //set the constant off pattern + chopper_config_register |= CHOPPER_MODE_T_OFF_FAST_DECAY; + //set the blank timing value + chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT; + //setting the constant off time + chopper_config_register |= constant_off_time; + //set the fast decay time + //set msb + chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x8)) << HYSTERESIS_DECREMENT_SHIFT); + //other bits + chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x7)) << HYSTERESIS_START_VALUE_SHIFT); + //set the sine wave offset + chopper_config_register |= (unsigned long)sine_wave_offset << HYSTERESIS_LOW_SHIFT; + //using the current comparator? + if (!use_current_comparator) { + chopper_config_register |= (1 << 12); + } + //if started we directly send it to the motor + if (started) { + send262(chopper_config_register); + } +} + +/* + * constant_off_time: The off time setting controls the minimum chopper frequency. + * For most applications an off time within the range of 5μs to 20μs will fit. + * 2...15: off time setting + * + * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the + * duration of the ringing on the sense resistor. For + * 0: min. setting 3: max. setting + * + * hysteresis_start: Hysteresis start setting. Please remark, that this value is an offset to the hysteresis end value HEND. + * 1...8 + * + * hysteresis_end: Hysteresis end setting. Sets the hysteresis end value after a number of decrements. Decrement interval time is controlled by HDEC. + * The sum HSTRT+HEND must be <16. At a current setting CS of max. 30 (amplitude reduced to 240), the sum is not limited. + * -3..-1: negative HEND 0: zero HEND 1...12: positive HEND + * + * hysteresis_decrement: Hysteresis decrement setting. This setting determines the slope of the hysteresis during on time and during fast decay time. + * 0: fast decrement 3: very slow decrement + */ + +void TMC26X::setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement) +{ + h_start = hysteresis_start; + h_end = hysteresis_end; + h_decrement = hysteresis_decrement; + this->blank_time = blank_time; + + //perform some sanity checks + if (constant_off_time < 2) { + constant_off_time = 2; + } else if (constant_off_time > 15) { + constant_off_time = 15; + } + //save the constant off time + this->constant_off_time = constant_off_time; + int8_t blank_value; + //calculate the value acc to the clock cycles + if (blank_time >= 54) { + blank_value = 3; + } else if (blank_time >= 36) { + blank_value = 2; + } else if (blank_time >= 24) { + blank_value = 1; + } else { + blank_value = 0; + } + + if (hysteresis_start < 1) { + hysteresis_start = 1; + } else if (hysteresis_start > 8) { + hysteresis_start = 8; + } + hysteresis_start--; + + if (hysteresis_end < -3) { + hysteresis_end = -3; + } else if (hysteresis_end > 12) { + hysteresis_end = 12; + } + //shift the hysteresis_end + hysteresis_end += 3; + + if (hysteresis_decrement < 0) { + hysteresis_decrement = 0; + } else if (hysteresis_decrement > 3) { + hysteresis_decrement = 3; + } + + //first of all delete all the values for this + chopper_config_register &= ~(CHOPPER_MODE_T_OFF_FAST_DECAY | BLANK_TIMING_PATTERN | HYSTERESIS_DECREMENT_PATTERN | HYSTERESIS_LOW_VALUE_PATTERN | HYSTERESIS_START_VALUE_PATTERN | T_OFF_TIMING_PATERN); + + //set the blank timing value + chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT; + //setting the constant off time + chopper_config_register |= constant_off_time; + //set the hysteresis_start + chopper_config_register |= ((unsigned long)hysteresis_start) << HYSTERESIS_START_VALUE_SHIFT; + //set the hysteresis end + chopper_config_register |= ((unsigned long)hysteresis_end) << HYSTERESIS_LOW_SHIFT; + //set the hystereis decrement + chopper_config_register |= ((unsigned long)hysteresis_decrement) << HYSTERESIS_DECREMENT_SHIFT; + + //if started we directly send it to the motor + if (started) { + send262(chopper_config_register); + } +} + +/* + * In a constant off time chopper scheme both coil choppers run freely, i.e. are not synchronized. + * The frequency of each chopper mainly depends on the coil current and the position dependant motor coil inductivity, thus it depends on the microstep position. + * With some motors a slightly audible beat can occur between the chopper frequencies, especially when they are near to each other. This typically occurs at a + * few microstep positions within each quarter wave. This effect normally is not audible when compared to mechanical noise generated by ball bearings, etc. + * Further factors which can cause a similar effect are a poor layout of sense resistor GND connection. + * Hint: A common factor, which can cause motor noise, is a bad PCB layout causing coupling of both sense resistor voltages + * (please refer to sense resistor layout hint in chapter 8.1). + * In order to minimize the effect of a beat between both chopper frequencies, an internal random generator is provided. + * It modulates the slow decay time setting when switched on by the RNDTF bit. The RNDTF feature further spreads the chopper spectrum, + * reducing electromagnetic emission on single frequencies. + */ +void TMC26X::setRandomOffTime(int8_t value) +{ + if (value) { + chopper_config_register |= RANDOM_TOFF_TIME; + } else { + chopper_config_register &= ~(RANDOM_TOFF_TIME); + } + //if started we directly send it to the motor + if (started) { + send262(chopper_config_register); + } +} + +void TMC26X::setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size, + uint8_t current_increment_step_size, uint8_t lower_current_limit) +{ + //sanitize the input values + if (lower_SG_threshold > 480) { + lower_SG_threshold = 480; + } + //divide by 32 + lower_SG_threshold >>= 5; + if (SG_hysteresis > 480) { + SG_hysteresis = 480; + } + //divide by 32 + SG_hysteresis >>= 5; + if (current_decrement_step_size > 3) { + current_decrement_step_size = 3; + } + if (current_increment_step_size > 3) { + current_increment_step_size = 3; + } + if (lower_current_limit > 1) { + lower_current_limit = 1; + } + //store the lower level in order to enable/disable the cool step + this->cool_step_lower_threshold = lower_SG_threshold; + //if cool step is not enabled we delete the lower value to keep it disabled + if (!this->cool_step_enabled) { + lower_SG_threshold = 0; + } + //the good news is that we can start with a complete new cool step register value + //and simply set the values in the register + cool_step_register_value = ((unsigned long)lower_SG_threshold) | (((unsigned long)SG_hysteresis) << 8) | (((unsigned long)current_decrement_step_size) << 5) + | (((unsigned long)current_increment_step_size) << 13) | (((unsigned long)lower_current_limit) << 15) + //and of course we have to include the signature of the register + | COOL_STEP_REGISTER; + + if (started) { + send262(cool_step_register_value); + } +} + +void TMC26X::setCoolStepEnabled(bool enabled) +{ + //simply delete the lower limit to disable the cool step + cool_step_register_value &= ~SE_MIN_PATTERN; + //and set it to the proper value if cool step is to be enabled + if (enabled) { + cool_step_register_value |= this->cool_step_lower_threshold; + } + //and save the enabled status + this->cool_step_enabled = enabled; + //save the register value + if (started) { + send262(cool_step_register_value); + } +} + +bool TMC26X::isCoolStepEnabled(void) +{ + return this->cool_step_enabled; +} + +unsigned int TMC26X::getCoolStepLowerSgThreshold() +{ + //we return our internally stored value - in order to provide the correct setting even if cool step is not enabled + return this->cool_step_lower_threshold << 5; +} + +unsigned int TMC26X::getCoolStepUpperSgThreshold() +{ + return (uint8_t)((cool_step_register_value & SE_MAX_PATTERN) >> 8) << 5; +} + +uint8_t TMC26X::getCoolStepCurrentIncrementSize() +{ + return (uint8_t)((cool_step_register_value & CURRENT_DOWN_STEP_SPEED_PATTERN) >> 13); +} + +uint8_t TMC26X::getCoolStepNumberOfSGReadings() +{ + return (uint8_t)((cool_step_register_value & SE_CURRENT_STEP_WIDTH_PATTERN) >> 5); +} + +uint8_t TMC26X::getCoolStepLowerCurrentLimit() +{ + return (uint8_t)((cool_step_register_value & MINIMUM_CURRENT_FOURTH) >> 15); +} + +void TMC26X::setEnabled(bool enabled) +{ + //delete the t_off in the chopper config to get sure + chopper_config_register &= ~(T_OFF_PATTERN); + if (enabled) { + //and set the t_off time + chopper_config_register |= this->constant_off_time; + } + //if not enabled we don't have to do anything since we already delete t_off from the register + if (started) { + send262(chopper_config_register); + } +} + +bool TMC26X::isEnabled() +{ + if (chopper_config_register & T_OFF_PATTERN) { + return true; + } else { + return false; + } +} + +/* + * reads a value from the TMC26X status register. The value is not obtained directly but can then + * be read by the various status routines. + * + */ +void TMC26X::readStatus(int8_t read_value) +{ + unsigned long old_driver_configuration_register_value = driver_configuration_register_value; + //reset the readout configuration + driver_configuration_register_value &= ~(READ_SELECTION_PATTERN); + //this now equals TMC26X_READOUT_POSITION - so we just have to check the other two options + if (read_value == TMC26X_READOUT_STALLGUARD) { + driver_configuration_register_value |= READ_STALL_GUARD_READING; + } else if (read_value == TMC26X_READOUT_CURRENT) { + driver_configuration_register_value |= READ_STALL_GUARD_AND_COOL_STEP; + } + //all other cases are ignored to prevent funny values + //check if the readout is configured for the value we are interested in + if (driver_configuration_register_value != old_driver_configuration_register_value) { + //because then we need to write the value twice - one time for configuring, second time to get the value, see below + send262(driver_configuration_register_value); + } + //write the configuration to get the last status + send262(driver_configuration_register_value); +} + +//reads the stall guard setting from last status +//returns -1 if stallguard information is not present +int TMC26X::getCurrentStallGuardReading(void) +{ + //if we don't yet started there cannot be a stall guard value + if (!started) { + return -1; + } + //not time optimal, but solution optiomal: + //first read out the stall guard value + readStatus(TMC26X_READOUT_STALLGUARD); + return getReadoutValue(); +} + +uint8_t TMC26X::getCurrentCSReading(void) +{ + //if we don't yet started there cannot be a stall guard value + if (!started) { + return 0; + } + + //first read out the stall guard value + readStatus(TMC26X_READOUT_CURRENT); + return (getReadoutValue() & 0x1f); +} + +unsigned int TMC26X::getCoolstepCurrent(void) +{ + float result = (float)getCurrentCSReading(); + float resistor_value = (float)this->resistor; + float voltage = (driver_configuration_register_value & VSENSE) ? 0.165F : 0.31F; + result = (result + 1.0F) / 32.0F * voltage / resistor_value * 1000.0F * 1000.0F; + return (unsigned int)roundf(result); +} + +/* + return true if the stallguard threshold has been reached +*/ +bool TMC26X::isStallGuardOverThreshold(void) +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_STALL_GUARD_STATUS); +} + +/* + returns if there is any over temperature condition: + OVER_TEMPERATURE_PREWARING if pre warning level has been reached + OVER_TEMPERATURE_SHUTDOWN if the temperature is so hot that the driver is shut down + Any of those levels are not too good. +*/ +int8_t TMC26X::getOverTemperature(void) +{ + if (!this->started) { + return 0; + } + if (driver_status_result & STATUS_OVER_TEMPERATURE_SHUTDOWN) { + return TMC26X_OVERTEMPERATURE_SHUTDOWN; + } + if (driver_status_result & STATUS_OVER_TEMPERATURE_WARNING) { + return TMC26X_OVERTEMPERATURE_PREWARING; + } + return 0; +} + +//is motor channel A shorted to ground +bool TMC26X::isShortToGroundA(void) +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_SHORT_TO_GROUND_A); +} + +//is motor channel B shorted to ground +bool TMC26X::isShortToGroundB(void) +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_SHORT_TO_GROUND_B); +} + +//is motor channel A connected +bool TMC26X::isOpenLoadA(void) +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_OPEN_LOAD_A); +} + +//is motor channel B connected +bool TMC26X::isOpenLoadB(void) +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_OPEN_LOAD_B); +} + +//is chopper inactive since 2^20 clock cycles - defaults to ~0,08s +bool TMC26X::isStandStill(void) +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_STAND_STILL); +} + +//is chopper inactive since 2^20 clock cycles - defaults to ~0,08s +bool TMC26X::isStallGuardReached(void) +{ + if (!this->started) { + return false; + } + return (driver_status_result & STATUS_STALL_GUARD_STATUS); +} + +//reads the stall guard setting from last status +//returns -1 if stallguard inforamtion is not present +int TMC26X::getReadoutValue(void) +{ + return (int)(driver_status_result >> 10); +} + +bool TMC26X::isCurrentScalingHalfed() +{ + if (this->driver_configuration_register_value & VSENSE) { + return true; + } else { + return false; + } +} + +void TMC26X::dumpStatus(StreamOutput *stream, bool readable) +{ + if (readable) { + stream->printf("designator %c, Chip type TMC26X\n", designator); + + check_error_status_bits(stream); + + if (this->isStallGuardReached()) { + stream->printf("INFO: Stall Guard level reached!\n"); + } + + if (this->isStandStill()) { + stream->printf("INFO: Motor is standing still.\n"); + } + + int value = getReadoutValue(); + stream->printf("Microstep postion phase A: %d\n", value); + + value = getCurrentStallGuardReading(); + stream->printf("Stall Guard value: %d\n", value); + + stream->printf("Current setting: %dmA\n", getCurrent()); + stream->printf("Coolstep current: %dmA\n", getCoolstepCurrent()); + + stream->printf("Microsteps: 1/%d\n", microsteps); + + stream->printf("Register dump:\n"); + stream->printf(" driver control register: %08lX(%ld)\n", driver_control_register_value, driver_control_register_value); + stream->printf(" chopper config register: %08lX(%ld)\n", chopper_config_register, chopper_config_register); + stream->printf(" cool step register: %08lX(%ld)\n", cool_step_register_value, cool_step_register_value); + stream->printf(" stall guard2 current register: %08lX(%ld)\n", stall_guard2_current_register_value, stall_guard2_current_register_value); + stream->printf(" driver configuration register: %08lX(%ld)\n", driver_configuration_register_value, driver_configuration_register_value); + stream->printf(" motor_driver_control.xxx.reg %05lX,%05lX,%05lX,%05lX,%05lX\n", driver_control_register_value, chopper_config_register, cool_step_register_value, stall_guard2_current_register_value, driver_configuration_register_value); + + } else { + // TODO hardcoded for X need to select ABC as needed + bool moving = THEKERNEL->robot->actuators[0]->is_moving(); + // dump out in the format that the processing script needs + if (moving) { + stream->printf("#sg%d,p%lu,k%u,r,", getCurrentStallGuardReading(), THEKERNEL->robot->actuators[0]->get_stepped(), getCoolstepCurrent()); + } else { + readStatus(TMC26X_READOUT_POSITION); // get the status bits + stream->printf("#s,"); + } + stream->printf("d%d,", THEKERNEL->robot->actuators[0]->which_direction() ? 1 : -1); + stream->printf("c%u,m%d,", getCurrent(), getMicrosteps()); + // stream->printf('S'); + // stream->printf(TMC26XStepper.getSpeed(), DEC); + stream->printf("t%d,f%d,", getStallGuardThreshold(), getStallGuardFilter()); + + //print out the general cool step config + if (isCoolStepEnabled()) stream->printf("Ke+,"); + else stream->printf("Ke-,"); + + stream->printf("Kl%u,Ku%u,Kn%u,Ki%u,Km%u,", + getCoolStepLowerSgThreshold(), getCoolStepUpperSgThreshold(), getCoolStepNumberOfSGReadings(), getCoolStepCurrentIncrementSize(), getCoolStepLowerCurrentLimit()); + + //detect the winding status + if (isOpenLoadA()) { + stream->printf("ao,"); + } else if(isShortToGroundA()) { + stream->printf("ag,"); + } else { + stream->printf("a-,"); + } + //detect the winding status + if (isOpenLoadB()) { + stream->printf("bo,"); + } else if(isShortToGroundB()) { + stream->printf("bg,"); + } else { + stream->printf("b-,"); + } + + char temperature = getOverTemperature(); + if (temperature == 0) { + stream->printf("x-,"); + } else if (temperature == TMC26X_OVERTEMPERATURE_PREWARING) { + stream->printf("xw,"); + } else { + stream->printf("xe,"); + } + + if (isEnabled()) { + stream->printf("e1,"); + } else { + stream->printf("e0,"); + } + + //write out the current chopper config + stream->printf("Cm%d,", (chopper_config_register & CHOPPER_MODE_T_OFF_FAST_DECAY) != 0); + stream->printf("Co%d,Cb%d,", constant_off_time, blank_time); + if ((chopper_config_register & CHOPPER_MODE_T_OFF_FAST_DECAY) == 0) { + stream->printf("Cs%d,Ce%d,Cd%d,", h_start, h_end, h_decrement); + } + stream->printf("\n"); + } +} + +// check error bits and report, only report once +bool TMC26X::check_error_status_bits(StreamOutput *stream) +{ + bool error= false; + readStatus(TMC26X_READOUT_POSITION); // get the status bits + + if (this->getOverTemperature()&TMC26X_OVERTEMPERATURE_PREWARING) { + if(!error_reported.test(0)) stream->printf("%c - WARNING: Overtemperature Prewarning!\n", designator); + error_reported.set(0); + }else{ + error_reported.reset(0); + } + + if (this->getOverTemperature()&TMC26X_OVERTEMPERATURE_SHUTDOWN) { + if(!error_reported.test(1)) stream->printf("%c - ERROR: Overtemperature Shutdown!\n", designator); + error=true; + error_reported.set(1); + }else{ + error_reported.reset(1); + } + + if (this->isShortToGroundA()) { + if(!error_reported.test(2)) stream->printf("%c - ERROR: SHORT to ground on channel A!\n", designator); + error=true; + error_reported.set(2); + }else{ + error_reported.reset(2); + } + + if (this->isShortToGroundB()) { + if(!error_reported.test(3)) stream->printf("%c - ERROR: SHORT to ground on channel B!\n", designator); + error=true; + error_reported.set(3); + }else{ + error_reported.reset(3); + } + + // these seem to be triggered when moving so ignore them for now + if (this->isOpenLoadA()) { + if(!error_reported.test(4)) stream->printf("%c - ERROR: Channel A seems to be unconnected!\n", designator); + error=true; + error_reported.set(4); + }else{ + error_reported.reset(4); + } + + if (this->isOpenLoadB()) { + if(!error_reported.test(5)) stream->printf("%c - ERROR: Channel B seems to be unconnected!\n", designator); + error=true; + error_reported.set(5); + }else{ + error_reported.reset(5); + } + + // if(error) { + // stream->printf("%08X\n", driver_status_result); + // } + return error; +} + +bool TMC26X::checkAlarm() +{ + return check_error_status_bits(THEKERNEL->streams); +} + +// sets a raw register to the value specified, for advanced settings +// register 255 writes them, 0 displays what registers are mapped to what +// FIXME status registers not reading back correctly, check docs +bool TMC26X::setRawRegister(StreamOutput *stream, uint32_t reg, uint32_t val) +{ + switch(reg) { + case 255: + send262(driver_control_register_value); + send262(chopper_config_register); + send262(cool_step_register_value); + send262(stall_guard2_current_register_value); + send262(driver_configuration_register_value); + stream->printf("Registers written\n"); + break; + + + case 1: driver_control_register_value = val; stream->printf("driver control register set to %08lX\n", val); break; + case 2: chopper_config_register = val; stream->printf("chopper config register set to %08lX\n", val); break; + case 3: cool_step_register_value = val; stream->printf("cool step register set to %08lX\n", val); break; + case 4: stall_guard2_current_register_value = val; stream->printf("stall guard2 current register set to %08lX\n", val); break; + case 5: driver_configuration_register_value = val; stream->printf("driver configuration register set to %08lX\n", val); break; + + default: + stream->printf("1: driver control register\n"); + stream->printf("2: chopper config register\n"); + stream->printf("3: cool step register\n"); + stream->printf("4: stall guard2 current register\n"); + stream->printf("5: driver configuration register\n"); + stream->printf("255: update all registers\n"); + return false; + } + return true; +} + +/* + * send register settings to the stepper driver via SPI + * returns the current status + * sends 20bits, the last 20 bits of the 24bits is taken as the command + */ +void TMC26X::send262(unsigned long datagram) +{ + uint8_t buf[] {(uint8_t)(datagram >> 16), (uint8_t)(datagram >> 8), (uint8_t)(datagram & 0xff)}; + uint8_t rbuf[3]; + + //write/read the values + spi(buf, 3, rbuf); + + // construct reply + unsigned long i_datagram = ((rbuf[0] << 16) | (rbuf[1] << 8) | (rbuf[2])) >> 4; + + //store the datagram as status result + driver_status_result = i_datagram; + + //THEKERNEL->streams->printf("sent: %02X, %02X, %02X received: %02X, %02X, %02X \n", buf[0], buf[1], buf[2], rbuf[0], rbuf[1], rbuf[2]); +} + +#define HAS(X) (options.find(X) != options.end()) +#define GET(X) (options.at(X)) +bool TMC26X::set_options(const options_t& options) +{ + bool set = false; + if(HAS('O') || HAS('Q')) { + // void TMC26X::setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled) + int8_t o = HAS('O') ? GET('O') : getStallGuardThreshold(); + int8_t q = HAS('Q') ? GET('Q') : getStallGuardFilter(); + setStallGuardThreshold(o, q); + set = true; + } + + if(HAS('H') && HAS('I') && HAS('J') && HAS('K') && HAS('L')) { + //void TMC26X::setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size, uint8_t current_increment_step_size, uint8_t lower_current_limit) + setCoolStepConfiguration(GET('H'), GET('I'), GET('J'), GET('K'), GET('L')); + set = true; + } + + if(HAS('S')) { + uint32_t s = GET('S'); + if(s == 0 && HAS('U') && HAS('V') && HAS('W') && HAS('X') && HAS('Y')) { + //void TMC26X::setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator) + setConstantOffTimeChopper(GET('U'), GET('V'), GET('W'), GET('X'), GET('Y')); + set = true; + + } else if(s == 1 && HAS('U') && HAS('V') && HAS('W') && HAS('X') && HAS('Y')) { + //void TMC26X::setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement); + setSpreadCycleChopper(GET('U'), GET('V'), GET('W'), GET('X'), GET('Y')); + set = true; + + } else if(s == 2 && HAS('Z')) { + setRandomOffTime(GET('Z')); + set = true; + + } else if(s == 3 && HAS('Z')) { + setDoubleEdge(GET('Z')); + set = true; + + } else if(s == 4 && HAS('Z')) { + setStepInterpolation(GET('Z')); + set = true; + + } else if(s == 5 && HAS('Z')) { + setCoolStepEnabled(GET('Z') == 1); + set = true; + } + } + + return set; +} diff --git a/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.h b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.h new file mode 100644 index 00000000..661d5973 --- /dev/null +++ b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.h @@ -0,0 +1,453 @@ +/* + modified from... + TMC26X.cpp - - TMC26X Stepper library for Wiring/Arduino + + based on the stepper library by Tom Igoe, et. al. + + Copyright (c) 2011, Interactive Matter, Marcus Nowotny + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + */ + + +// ensure this library description is only included once +#pragma once + +#include +#include +#include + +class StreamOutput; + +#define TMC2660_max_current 4000 + +/*! + * \class TMC26X + * \brief Class representing a TMC26X stepper driver + */ +class TMC26X +{ +public: + /*! + * \brief creates a new represenatation of a stepper motor connected to a TMC26X stepper driver + * + * This is the main constructor. If in doubt use this. You must provide all parameters as described below. + * + * \param spi send function + * + * By default the Constant Off Time chopper is used, see TCM2630Stepper.setConstantOffTimeChopper() for details. + * This should work on most motors (YMMV). You may want to configure and use the Spread Cycle Chopper, see setSpreadCycleChopper(). + * + * By default a microstepping of 1/32th is used to provide a smooth motor run, while still giving a good progression per step. + * You can select a different stepping with setMicrosteps() to aa different value. + * \sa start(), setMicrosteps() + */ + TMC26X(std::function spi, char designator); + + /*! + * \brief configures the TMC26X stepper driver. Before you called this function the stepper driver is in nonfunctional mode. + * + * \param rms_current the maximum current to privide to the motor in mA (!). A value of 200 would send up to 200mA to the motor + * \param resistor the current sense resistor in milli Ohm, defaults to ,15 Ohm ( or 150 milli Ohm) as in the TMC260 Arduino Shield + + * This routine configures the TMC26X stepper driver for the given values via SPI. + * Most member functions are non functional if the driver has not been started. + * Therefore it is best to call this in your Arduino setup() function. + */ + void init(uint16_t cs); + + /*! + * \brief Set the number of microsteps in 2^i values (rounded) up to 256 + * + * This method set's the number of microsteps per step in 2^i interval. + * This means you can select 1, 2, 4, 16, 32, 64, 128 or 256 as valid microsteps. + * If you give any other value it will be rounded to the next smaller number (3 would give a microstepping of 2). + * You can always check the current microstepping with getMicrosteps(). + */ + void setMicrosteps(int number_of_steps); + + /*! + * \brief returns the effective current number of microsteps selected. + * + * This function always returns the effective number of microsteps. + * This can be a bit different than the micro steps set in setMicrosteps() since it is rounded to 2^i. + * + * \sa setMicrosteps() + */ + int getMicrosteps(void); + + void setStepInterpolation(int8_t value); + void setDoubleEdge(int8_t value); + + /*! + * \brief Sets and configure the classical Constant Off Timer Chopper + * \param constant_off_time The off time setting controls the minimum chopper frequency. For most applications an off time within the range of 5μs to 20μs will fit. Setting this parameter to zero completely disables all driver transistors and the motor can free-wheel. 0: chopper off, 1:15: off time setting (1 will work with minimum blank time of 24 clocks) + * \param blank_time Selects the comparator blank time. This time needs to safely cover the switching event and the duration of the ringing on the sense resistor. For most low current drivers, a setting of 1 or 2 is good. For high current applications with large MOSFETs, a setting of 2 or 3 will be required. 0 (min setting) … (3) amx setting + * \param fast_decay_time_setting Fast decay time setting. Controls the portion of fast decay for each chopper cycle. 0: slow decay only, 1…15: duration of fast decay phase + * \param sine_wave_offset Sine wave offset. Controls the sine wave offset. A positive offset corrects for zero crossing error. -3…-1: negative offset, 0: no offset,1…12: positive offset + * \param use_curreent_comparator Selects usage of the current comparator for termination of the fast decay cycle. If current comparator is enabled, it terminates the fast decay cycle in case the current reaches a higher negative value than the actual positive value. (0 disable, -1 enable). + * + * The classic constant off time chopper uses a fixed portion of fast decay following each on phase. + * While the duration of the on time is determined by the chopper comparator, the fast decay time needs + * to be set by the user in a way, that the current decay is enough for the driver to be able to follow + * the falling slope of the sine wave, and on the other hand it should not be too long, in order to minimize + * motor current ripple and power dissipation. This best can be tuned using an oscilloscope or + * trying out motor smoothness at different velocities. A good starting value is a fast decay time setting + * similar to the slow decay time setting. + * After tuning of the fast decay time, the offset should be determined, in order to have a smooth zero transition. + * This is necessary, because the fast decay phase leads to the absolute value of the motor current being lower + * than the target current (see figure 17). If the zero offset is too low, the motor stands still for a short + * moment during current zero crossing, if it is set too high, it makes a larger microstep. + * Typically, a positive offset setting is required for optimum operation. + * + * \sa setSpreadCycleChoper() for other alternatives. + * \sa setRandomOffTime() for spreading the noise over a wider spectrum + */ + void setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator); + + /*! + * \brief Sets and configures with spread cycle chopper. + * \param constant_off_time The off time setting controls the minimum chopper frequency. For most applications an off time within the range of 5μs to 20μs will fit. Setting this parameter to zero completely disables all driver transistors and the motor can free-wheel. 0: chopper off, 1:15: off time setting (1 will work with minimum blank time of 24 clocks) + * \param blank_time Selects the comparator blank time. This time needs to safely cover the switching event and the duration of the ringing on the sense resistor. For most low current drivers, a setting of 1 or 2 is good. For high current applications with large MOSFETs, a setting of 2 or 3 will be required. 0 (min setting) … (3) amx setting + * \param hysteresis_start Hysteresis start setting. Please remark, that this value is an offset to the hysteresis end value. 1 … 8 + * \param hysteresis_end Hysteresis end setting. Sets the hysteresis end value after a number of decrements. Decrement interval time is controlled by hysteresis_decrement. The sum hysteresis_start + hysteresis_end must be <16. At a current setting CS of max. 30 (amplitude reduced to 240), the sum is not limited. + * \param hysteresis_decrement Hysteresis decrement setting. This setting determines the slope of the hysteresis during on time and during fast decay time. 0 (fast decrement) … 3 (slow decrement). + * + * The spreadCycle chopper scheme (pat.fil.) is a precise and simple to use chopper principle, which automatically determines + * the optimum fast decay portion for the motor. Anyhow, a number of settings can be made in order to optimally fit the driver + * to the motor. + * Each chopper cycle is comprised of an on-phase, a slow decay phase, a fast decay phase and a second slow decay phase. + * The slow decay phases limit the maximum chopper frequency and are important for low motor and driver power dissipation. + * The hysteresis start setting limits the chopper frequency by forcing the driver to introduce a minimum amount of + * current ripple into the motor coils. The motor inductivity determines the ability to follow a changing motor current. + * The duration of the on- and fast decay phase needs to cover at least the blank time, because the current comparator is + * disabled during this time. + * + * \sa setRandomOffTime() for spreading the noise over a wider spectrum + */ + void setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement); + + /*! + * \brief Use random off time for noise reduction (0 for off, -1 for on). + * \param value 0 for off, -1 for on + * + * In a constant off time chopper scheme both coil choppers run freely, i.e. are not synchronized. + * The frequency of each chopper mainly depends on the coil current and the position dependant motor coil inductivity, + * thus it depends on the microstep position. With some motors a slightly audible beat can occur between the chopper + * frequencies, especially when they are near to each other. This typically occurs at a few microstep positions within + * each quarter wave. + * This effect normally is not audible when compared to mechanical noise generated by ball bearings, + * etc. Further factors which can cause a similar effect are a poor layout of sense resistor GND connection. + * In order to minimize the effect of a beat between both chopper frequencies, an internal random generator is provided. + * It modulates the slow decay time setting when switched on. The random off time feature further spreads the chopper spectrum, + * reducing electromagnetic emission on single frequencies. + */ + void setRandomOffTime(int8_t value); + + /*! + * \brief set the maximum motor current in mA (1000 is 1 Amp) + * Keep in mind this is the maximum peak Current. The RMS current will be 1/sqrt(2) smaller. The actual current can also be smaller + * by employing CoolStep. + * \param current the maximum motor current in mA + * \sa getCurrent(), getCurrentCurrent() + */ + void setCurrent(unsigned int current); + + /*! + * \brief readout the motor maximum current in mA (1000 is an Amp) + * This is the maximum current. to get the current current - which may be affected by CoolStep us getCurrentCurrent() + *\return the maximum motor current in milli amps + * \sa getCurrentCurrent() + */ + unsigned int getCurrent(void); + + /*! + * \brief set the StallGuard threshold in order to get sensible StallGuard readings. + * \param stall_guard_threshold -64 … 63 the StallGuard threshold + * \param stall_guard_filter_enabled 0 if the filter is disabled, -1 if it is enabled + * + * The StallGuard threshold is used to optimize the StallGuard reading to sensible values. It should be at 0 at + * the maximum allowable load on the motor (but not before). = is a good starting point (and the default) + * If you get Stall Gaurd readings of 0 without any load or with too little load increase the value. + * If you get readings of 1023 even with load decrease the setting. + * + * If you switch on the filter the StallGuard reading is only updated each 4th full step to reduce the noise in the + * reading. + * + * \sa getCurrentStallGuardReading() to read out the current value. + */ + void setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled); + + /*! + * \brief reads out the StallGuard threshold + * \return a number between -64 and 63. + */ + int8_t getStallGuardThreshold(void); + + /*! + * \brief returns the current setting of the StallGuard filter + * \return 0 if not set, -1 if set + */ + int8_t getStallGuardFilter(void); + + /*! + * \brief This method configures the CoolStep smart energy operation. You must have a proper StallGuard configuration for the motor situation (current, voltage, speed) in rder to use this feature. + * \param lower_SG_threshold Sets the lower threshold for stallGuard2TM reading. Below this value, the motor current becomes increased. Allowed values are 0...480 + * \param SG_hysteresis Sets the distance between the lower and the upper threshold for stallGuard2TM reading. Above the upper threshold (which is lower_SG_threshold+SG_hysteresis+1) the motor current becomes decreased. Allowed values are 0...480 + * \param current_decrement_step_size Sets the current decrement steps. If the StallGuard value is above the threshold the current gets decremented by this step size. 0...32 + * \param current_increment_step_size Sets the current increment step. The current becomes incremented for each measured stallGuard2TM value below the lower threshold. 0...8 + * \param lower_current_limit Sets the lower motor current limit for coolStepTM operation by scaling the CS value. Values can be COOL_STEP_HALF_CS_LIMIT, COOL_STEP_QUARTER_CS_LIMIT + * The CoolStep smart energy operation automatically adjust the current sent into the motor according to the current load, + * read out by the StallGuard in order to provide the optimum torque with the minimal current consumption. + * You configure the CoolStep current regulator by defining upper and lower bounds of StallGuard readouts. If the readout is above the + * limit the current gets increased, below the limit the current gets decreased. + * You can specify the upper an lower threshold of the StallGuard readout in order to adjust the current. You can also set the number of + * StallGuard readings neccessary above or below the limit to get a more stable current adjustement. + * The current adjustement itself is configured by the number of steps the current gests in- or decreased and the absolut minimum current + * (1/2 or 1/4th otf the configured current). + * \sa COOL_STEP_HALF_CS_LIMIT, COOL_STEP_QUARTER_CS_LIMIT + */ + void setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size, + uint8_t current_increment_step_size, uint8_t lower_current_limit); + + /*! + * \brief enables or disables the CoolStep smart energy operation feature. It must be configured before enabling it. + * \param enabled true if CoolStep should be enabled, false if not. + * \sa setCoolStepConfiguration() + */ + void setCoolStepEnabled(bool enabled); + + + /*! + * \brief check if the CoolStep feature is enabled + * \sa setCoolStepEnabled() + */ + bool isCoolStepEnabled(); + + /*! + * \brief returns the lower StallGuard threshold for the CoolStep operation + * \sa setCoolStepConfiguration() + */ + unsigned int getCoolStepLowerSgThreshold(); + + /*! + * \brief returns the upper StallGuard threshold for the CoolStep operation + * \sa setCoolStepConfiguration() + */ + unsigned int getCoolStepUpperSgThreshold(); + + /*! + * \brief returns the number of StallGuard readings befor CoolStep adjusts the motor current. + * \sa setCoolStepConfiguration() + */ + uint8_t getCoolStepNumberOfSGReadings(); + + /*! + * \brief returns the increment steps for the current for the CoolStep operation + * \sa setCoolStepConfiguration() + */ + uint8_t getCoolStepCurrentIncrementSize(); + + /*! + * \brief returns the absolut minium current for the CoolStep operation + * \sa setCoolStepConfiguration() + * \sa COOL_STEP_HALF_CS_LIMIT, COOL_STEP_QUARTER_CS_LIMIT + */ + uint8_t getCoolStepLowerCurrentLimit(); + + /*! + * \brief Reads the current StallGuard value. + * \return The current StallGuard value, lesser values indicate higher load, 0 means stall detected. + * Keep in mind that this routine reads and writes a value via SPI - so this may take a bit time. + * \sa setStallGuardThreshold() for tuning the readout to sensible ranges. + */ + int getCurrentStallGuardReading(void); + + /*! + * \brief Reads the current current setting value as fraction of the maximum current + * Returns values between 0 and 31, representing 1/32 to 32/32 (=1) + * \sa setCoolStepConfiguration() + */ + uint8_t getCurrentCSReading(void); + + + /*! + *\brief a convenience method to determine if the current scaling uses 0.31V or 0.165V as reference. + *\return false if 0.13V is the reference voltage, true if 0.165V is used. + */ + bool isCurrentScalingHalfed(); + + /*! + * \brief Reads the current current setting value and recalculates the absolute current in mA (1A would be 1000). + * This method calculates the currently used current setting (either by setting or by CoolStep) and reconstructs + * the current in mA by usinge the VSENSE and resistor value. This method uses floating point math - so it + * may not be the fastest. + * \sa getCurrentCSReading(), getResistor(), isCurrentScalingHalfed(), getCurrent() + */ + unsigned int getCoolstepCurrent(void); + + /*! + * \brief checks if there is a StallGuard warning in the last status + * \return 0 if there was no warning, -1 if there was some warning. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + * + * \sa setStallGuardThreshold() for tuning the readout to sensible ranges. + */ + bool isStallGuardOverThreshold(void); + + /*! + * \brief Return over temperature status of the last status readout + * return 0 is everything is OK, TMC26X_OVERTEMPERATURE_PREWARING if status is reached, TMC26X_OVERTEMPERATURE_SHUTDOWN is the chip is shutdown, -1 if the status is unknown. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + int8_t getOverTemperature(void); + + /*! + * \brief Is motor channel A shorted to ground detected in the last status readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + + bool isShortToGroundA(void); + + /*! + * \brief Is motor channel B shorted to ground detected in the last status readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isShortToGroundB(void); + /*! + * \brief iIs motor channel A connected according to the last statu readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isOpenLoadA(void); + + /*! + * \brief iIs motor channel A connected according to the last statu readout. + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isOpenLoadB(void); + + /*! + * \brief Is chopper inactive since 2^20 clock cycles - defaults to ~0,08s + * \return true is yes, false if not. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + */ + bool isStandStill(void); + + /*! + * \brief checks if there is a StallGuard warning in the last status + * \return 0 if there was no warning, -1 if there was some warning. + * Keep in mind that this method does not enforce a readout but uses the value of the last status readout. + * You may want to use getMotorPosition() or getCurrentStallGuardReading() to enforce an updated status readout. + * + * \sa isStallGuardOverThreshold() + * TODO why? + * + * \sa setStallGuardThreshold() for tuning the readout to sensible ranges. + */ + bool isStallGuardReached(void); + + /*! + *\brief enables or disables the motor driver bridges. If disabled the motor can run freely. If enabled not. + *\param enabled a bool value true if the motor should be enabled, false otherwise. + */ + void setEnabled(bool enabled); + + /*! + *\brief checks if the output bridges are enabled. If the bridges are not enabled the motor can run freely + *\return true if the bridges and by that the motor driver are enabled, false if not. + *\sa setEnabled() + */ + bool isEnabled(); + + /*! + * \brief Manually read out the status register + * This function sends a byte to the motor driver in order to get the current readout. The parameter read_value + * seletcs which value will get returned. If the read_vlaue changes in respect to the previous readout this method + * automatically send two bytes to the motor: one to set the redout and one to get the actual readout. So this method + * may take time to send and read one or two bits - depending on the previous readout. + * \param read_value selects which value to read out (0..3). You can use the defines TMC26X_READOUT_POSITION, TMC_2630_READOUT_STALLGUARD, or TMC_2630_READOUT_CURRENT + * \sa TMC26X_READOUT_POSITION, TMC_2630_READOUT_STALLGUARD, TMC_2630_READOUT_CURRENT + */ + void readStatus(int8_t read_value); + + /*! + * \brief Prints out all the information that can be found in the last status read out - it does not force a status readout. + * The result is printed via Serial + */ + void dumpStatus(StreamOutput *stream, bool readable= true); + bool setRawRegister(StreamOutput *stream, uint32_t reg, uint32_t val); + bool checkAlarm(); + + using options_t= std::map; + + bool set_options(const options_t& options); + +private: + //helper routione to get the top 10 bit of the readout + inline int getReadoutValue(); + bool check_error_status_bits(StreamOutput *stream); + + // SPI sender + inline void send262(unsigned long datagram); + std::function spi; + + unsigned int resistor{50}; // current sense resitor value in milliohm + + //driver control register copies to easily set & modify the registers + unsigned long driver_control_register_value; + unsigned long chopper_config_register; + unsigned long cool_step_register_value; + unsigned long stall_guard2_current_register_value; + unsigned long driver_configuration_register_value; + //the driver status result + unsigned long driver_status_result; + + //status values + int microsteps; //the current number of micro steps + + std::bitset<8> error_reported; + + // only needed for the tuning app report + struct { + int8_t blank_time:8; + int8_t constant_off_time:5; //we need to remember this value in order to enable and disable the motor + int8_t h_start:4; + int8_t h_end:4; + int8_t h_decrement:3; + bool cool_step_enabled:1; //we need to remember this to configure the coolstep if it si enabled + bool started:1; //if the stepper has been started yet + }; + + uint8_t cool_step_lower_threshold; // we need to remember the threshold to enable and disable the CoolStep feature + char designator; + +}; + diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 00000000..cc315639 --- /dev/null +++ b/src/version.cpp @@ -0,0 +1,11 @@ +#include "version.h" +const char *Version::get_build(void) const { + #ifdef DISABLEMSD + return __GITVERSIONSTRING__ "NOMSD"; + #else + return __GITVERSIONSTRING__; + #endif +} +const char *Version::get_build_date(void) const { + return __DATE__ " " __TIME__; +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 00000000..01f01a56 --- /dev/null +++ b/src/version.h @@ -0,0 +1,8 @@ +#ifndef _VERSION__H +#define _VERSION__H +class Version { + public: + const char *get_build(void) const; + const char *get_build_date(void) const; +}; +#endif From d5abca59d8892a9fd5a6a1df5daa335e47f9a4e5 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 22 Oct 2016 16:54:12 +1300 Subject: [PATCH 02/66] modified: src/config.default modified: src/libs/Pin.cpp modified: src/libs/Pin.h modified: src/libs/StepTicker.cpp modified: src/libs/StepperMotor.cpp modified: src/libs/StepperMotor.h modified: src/libs/checksumm.h modified: src/libs/nuts_bolts.h modified: src/libs/utils.h modified: src/main.cpp modified: src/makefile modified: src/modules/robot/Planner.cpp modified: src/modules/robot/Robot.cpp modified: src/modules/robot/Robot.h modified: src/modules/robot/Stepper.cpp modified: src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp modified: src/modules/tools/endstops/Endstops.cpp modified: src/modules/tools/temperaturecontrol/max31855.cpp --- src/config.default | 255 +++++++++++++----- src/libs/Pin.cpp | 46 ++-- src/libs/Pin.h | 8 +- src/libs/StepTicker.cpp | 12 +- src/libs/StepperMotor.cpp | 5 - src/libs/StepperMotor.h | 2 - src/libs/checksumm.h | 5 +- src/libs/nuts_bolts.h | 4 + src/libs/utils.h | 2 + src/main.cpp | 8 +- src/makefile | 10 +- src/modules/robot/Planner.cpp | 4 +- src/modules/robot/Robot.cpp | 5 +- src/modules/robot/Robot.h | 12 + src/modules/robot/Stepper.cpp | 6 +- .../arm_solutions/RotaryDeltaSolution.cpp | 1 - src/modules/tools/endstops/Endstops.cpp | 17 +- .../tools/temperaturecontrol/max31855.cpp | 10 +- 18 files changed, 291 insertions(+), 121 deletions(-) diff --git a/src/config.default b/src/config.default index 2d4a4418..dcd04a8c 100644 --- a/src/config.default +++ b/src/config.default @@ -1,7 +1,20 @@ +# NOTE Lines must not exceed 132 characters +# Robot module configurations : general handling of movement G-codes and slicing into moves +default_feed_rate 4000 # Default rate ( mm/minute ) for G1/G2/G3 moves +default_seek_rate 4000 # Default rate ( mm/minute ) for G0 moves +mm_per_arc_segment 0.0 # Fixed length for line segments that divide arcs 0 to disable +mm_max_arc_error 0.01 # The maximum error for line segments that divide arcs 0 to disable + # note it is invalid for both the above be 0 + # if both are used, will use largest segment length based on radius +#mm_per_line_segment 0.5 # Lines can be cut into segments ( not useful with cartesian + # coordinates robots ). +delta_segments_per_second 100 # for deltas only same as in Marlin/Delta, set to 0 to disable + # and use mm_per_line_segment + # Arm solution configuration : Cartesian robot. Translates mm positions into stepper positions -alpha_steps_per_mm 80 # Steps per mm for alpha stepper -beta_steps_per_mm 80 # Steps per mm for beta stepper -gamma_steps_per_mm 80 # Steps per mm for gamma stepper +#alpha_steps_per_mm 80 # Steps per mm for alpha stepper +#beta_steps_per_mm 80 # Steps per mm for beta stepper +#gamma_steps_per_mm 80 # Steps per mm for gamma stepper # Planner module configuration : Look-ahead and acceleration configuration planner_queue_size 32 # DO NOT CHANGE THIS UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING @@ -18,32 +31,114 @@ junction_deviation 0.05 # Similar to the o # Stepper module configuration microseconds_per_step_pulse 5 # Duration of step pulses to stepper drivers, in microseconds -base_stepping_frequency 100000 # Base frequency for stepping +base_stepping_frequency 10000 #100000 # Base frequency for stepping # Cartesian axis speed limits x_axis_max_speed 30000 # mm/min y_axis_max_speed 30000 # mm/min z_axis_max_speed 30000 # mm/min +# Arm solution configuration : Rotatable Delta robot. Translates mm positions into stepper positions +arm_solution rotary_delta # selects the delta arm solution + +delta_e 131.636 # End effector length, triangle side length in mm +delta_f 190.526 # Base length, triangle side length in mm +delta_re 270.000 # Carbon rod length +delta_rf 90.000 # Servo horn length + +delta_z_offset 268.0 # Distance from big pulley shaft to table/bed +delta_ee_offs 15.000 # Ball joint plane to bottom of end effector surface +delta_tool_offset 30.500 # Distance between end effector ball joint plane and tip of tool (PnP) + +delta_mirror_xy true # true for firepick + +# For a FirePick Rotary Delta, the steps per degree are calculated as: +# +# (1) First determine the circumference of the big pulley, which has a smooth surface, and calculate the 'teeth it would have' +# (2) Determine the GT2 belt thickness and tooth depth +# (3) Calculate the ACTUAL big pulley circumference taking into account that the GT2 belt affects this as +# it rests on the big pulley and not into teeth cut into the pulley, the formula is: +# BIG_PULLEY_CIRCUMFERENCE = BIG_PULLEY_TEETH * TOOTH_SPACING + (BELT_THICKNESS - TOOTH_DEPTH)*6.283185 +# (4) Calculate the ACTUAL small pulley circumference taking into account that the GT2 belt affects this as +# rests into the small pulley as it has teeth cut into the pulley, the formula is: +# SMALL_PULLEY_CIRCUMFERENCE = SMALL_PULLEY_TEETH * TOOTH_SPACING +# (5) Now determine the pulley reduction which is: +# PULLEY_REDUCTION = BIG_PULLEY_CIRCUMFERENCE / SMALL_PULLEY_CIRCUMFERENCE +# (6) Finally, given the number of steps a stepper motor needs for a full 360 degree rotation, and the number of microsteps your +# stepper motor driver chip is set for, you can calculate the STEPS_PER_MM for each axis, which for a Rotary Delta, are the same: +# [ALPHA/BETA/GAMMA]_STEPS_PER_MM = (XYZ_STEPS_PER_ROTATION*XYZ_MICROSTEPS*PULLEY_REDUCTION)/360 +# e.g. a FirePick Delta mechanism, we can calculate: +# BIG_PULLEY_CIRCUMFERENCE = 150 * 2 + (1.49 - 0.74) * 6.283185 = 304.712389 +# SMALL_PULLEY_CIRCUMFERENCE = 16 * 2 = 32 +# PULLEY_REDUCTION = 304.712389 / 32 = 9.52226215 +# For a 0.9 degree stepper motor and an a4988 driver set for 16 microsteps = (400 * 16 * 9.52226215)/360 = 169.2846604 +# For a 1.8 degree stepper motor and an a4988 driver set for 16 microsteps = (200 * 16 * 9.52226215)/360 = 84.6423302 +# For a 0.9 degree stepper motor and an drv8825 driver set for 32 microsteps = (400 * 32 * 9.52226215)/360 = 338.5693208 +# For a 1.8 degree stepper motor and an drv8825 driver set for 32 microsteps = (200 * 32 * 9.52226215)/360 = 169.2846604 +# For a 0.9 degree stepper motor and an TMC2130 driver set for 256 microsteps = (400 * 256 * 9.52226215)/360 = 2708.554567 +# For a 1.8 degree stepper motor and an TMC2130 driver set for 256 microsteps = (200 * 256 * 9.52226215)/360 = 1354.277284 + +alpha_steps_per_mm 338.0 # Steps per mm for alpha stepper +beta_steps_per_mm 338.0 # Steps per mm for beta stepper +gamma_steps_per_mm 338.0 # Steps per mm for gamma stepper + # Stepper module pins ( ports, and pin numbers, appending "!" to the number will invert a pin ) -alpha_step_pin 3.2 # Pin for alpha stepper step signal -alpha_dir_pin 7.3 # Pin for alpha stepper direction -alpha_en_pin 7.2 # Pin for alpha enable pin +alpha_step_pin 5.0 # Pin for alpha stepper step signal +alpha_dir_pin 6.12 # Pin for alpha stepper direction +alpha_en_pin 4.10 # Pin for alpha enable pin alpha_current 1.5 # X stepper motor current alpha_max_rate 30000.0 # mm/min -beta_step_pin 4.2 # Pin for beta stepper step signal -beta_dir_pin 7.0 # Pin for beta stepper direction -beta_en_pin 4.5 # Pin for beta enable +beta_step_pin 5.7 # Pin for beta stepper step signal +beta_dir_pin 5.5 # Pin for beta stepper direction +beta_en_pin 7.6 # Pin for beta enable beta_current 1.5 # Y stepper motor current beta_max_rate 30000.0 # mm/min -gamma_step_pin 5.7 # Pin for gamma stepper step signal -gamma_dir_pin 5.5 # Pin for gamma stepper direction -gamma_en_pin 7.6 # Pin for gamma enable +gamma_step_pin 4.0 # Pin for gamma stepper step signal +gamma_dir_pin 1.7 # Pin for gamma stepper direction +gamma_en_pin 6.9 # Pin for gamma enable gamma_current 1.5 # Z stepper motor current gamma_max_rate 30000.0 # mm/min +#SilentStepStick TMC2130 setup +motor_driver_control.alpha.enable true # alpha (X) is a TMC2130 +motor_driver_control.alpha.designator A # A to set the settings +motor_driver_control.alpha.chip TMC2130 # chip name +motor_driver_control.alpha.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.alpha.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.alpha.microsteps 256 # microsteps +motor_driver_control.alpha.alarm true # set to true means the error bits are checked +motor_driver_control.alpha.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set +motor_driver_control.alpha.spi_channel 0 # SPI channel 1 is sdcard channel +motor_driver_control.alpha.spi_cs_pin 7.4 # SPI CS pin +#motor_driver_control.alpha.spi_frequency 1000000 # SPI frequency + +motor_driver_control.beta.enable true # beta (Y) is a TMC2130 +motor_driver_control.beta.designator B # B to set the settings +motor_driver_control.beta.chip TMC2130 # chip name +motor_driver_control.beta.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.beta.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.beta.microsteps 256 # microsteps +motor_driver_control.beta.alarm true # set to true means the error bits are checked +motor_driver_control.beta.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set +motor_driver_control.beta.spi_channel 0 # SPI channel 1 is sdcard channel +motor_driver_control.beta.spi_cs_pin 7.5 # SPI CS pin +#motor_driver_control.beta.spi_frequency 1000000 # SPI frequency + +motor_driver_control.gamma.enable true # gamma (Z) is a TMC2130 +motor_driver_control.gamma.designator C # C to set the settings +motor_driver_control.gamma.chip TMC2130 # chip name +motor_driver_control.gamma.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.gamma.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.gamma.microsteps 256 # microsteps +motor_driver_control.gamma.alarm true # set to true means the error bits are checked +motor_driver_control.gamma.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set +motor_driver_control.gamma.spi_channel 0 # SPI channel 1 is sdcard channel ??? +motor_driver_control.gamma.spi_cs_pin 1.0 # SPI CS pin +#motor_driver_control.gamma.spi_frequency 1000000 # SPI frequency + + ## System configuration # Serial communications configuration ( baud rate defaults to 9600 if undefined ) uart0.baud_rate 115200 # Baud rate for the default hardware serial port @@ -53,7 +148,7 @@ second_usb_serial_enable false # This enables a s #play_led_disable true # disable the play led # Kill button (used to be called pause) maybe assigned to a different pin, set to the onboard pin by default -kill_button_enable false # set to true to enable a kill button +kill_button_enable false # set to true to enable a kill button kill_button_pin 2.12 # kill button pin. default is same as pause button 2.12 (2.11 is another good choice) #msd_disable false # disable the MSD (USB SDCARD) when set to true (needs special binary) @@ -61,30 +156,30 @@ kill_button_pin 2.12 # kill button pin. #watchdog_timeout 10 # watchdog timeout in seconds, default is 10, set to 0 to disable the watchdog # Only needed on a smoothieboard -currentcontrol_module_enable false # +currentcontrol_module_enable false # ## Extruder module configuration -extruder.hotend.enable true # Whether to activate the extruder module at all. All configuration is ignored if false -extruder.hotend.steps_per_mm 140 # Steps per mm for extruder stepper -extruder.hotend.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves -extruder.hotend.acceleration 500 # Acceleration for the stepper motor mm/sec² -extruder.hotend.max_speed 50 # mm/s +extruder.hotend.enable false # Whether to activate the extruder module at all. All configuration is ignored if false +extruder.hotend.steps_per_mm 140 # Steps per mm for extruder stepper +extruder.hotend.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves +extruder.hotend.acceleration 500 # Acceleration for the stepper motor mm/sec² +extruder.hotend.max_speed 50 # mm/s -extruder.hotend.step_pin 6.4 # Pin for extruder step signal -extruder.hotend.dir_pin 6.5 # Pin for extruder dir signal -extruder.hotend.en_pin 1.7 # Pin for extruder enable signal +extruder.hotend.step_pin 6.4 # Pin for extruder step signal +extruder.hotend.dir_pin 6.5 # Pin for extruder dir signal +extruder.hotend.en_pin 1.7 # Pin for extruder enable signal # Second extruder module configuration -extruder.hotend2.enable false # Whether to activate the extruder module at all. All configuration is ignored if false -extruder.hotend2.steps_per_mm 140 # Steps per mm for extruder stepper -extruder.hotend2.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves -extruder.hotend2.acceleration 500 # Acceleration for the stepper motor, as of 0.6, arbitrary ratio -extruder.hotend2.max_speed 50 # mm/s +extruder.hotend2.enable false # Whether to activate the extruder module at all. All configuration is ignored if false +extruder.hotend2.steps_per_mm 140 # Steps per mm for extruder stepper +extruder.hotend2.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves +extruder.hotend2.acceleration 500 # Acceleration for the stepper motor, as of 0.6, arbitrary ratio +extruder.hotend2.max_speed 50 # mm/s -extruder.hotend2.step_pin 6.10 # Pin for extruder step signal -extruder.hotend2.dir_pin 2.9 # Pin for extruder dir signal -extruder.hotend2.en_pin 2.8 # Pin for extruder enable signal +extruder.hotend2.step_pin 6.10 # Pin for extruder step signal +extruder.hotend2.dir_pin 2.9 # Pin for extruder dir signal +extruder.hotend2.en_pin 2.8 # Pin for extruder enable signal ## Laser module configuration @@ -92,9 +187,9 @@ laser_module_enable false # Whether to activ # ignored if false. ## Temperature control configuration # First hotend configuration -temperature_control.hotend.enable true # Whether to activate this ( "hotend" ) module at all. +temperature_control.hotend.enable false # Whether to activate this ( "hotend" ) module at all. # All configuration is ignored if false. -temperature_control.hotend.thermistor_pin adc.5 # Pin for the thermistor to read +temperature_control.hotend.thermistor_pin adc.5 # Pin for the thermistor to read temperature_control.hotend.heater_pin 7.5 # Pin that controls the heater, set to nc if a readonly thermistor is being defined temperature_control.hotend.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5 #temperature_control.hotend.beta 4066 # or set the beta value @@ -114,22 +209,22 @@ temperature_control.hotend.designator T # temperature_control.hotend2.enable false # Whether to activate this ( "hotend" ) module at all. # All configuration is ignored if false. -temperature_control.hotend2.thermistor_pin adc.7 # Pin for the thermistor to read -temperature_control.hotend2.heater_pin 7.7 # Pin that controls the heater -temperature_control.hotend2.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5 -#temperature_control.hotend2.beta 4066 # or set the beta value -temperature_control.hotend2.set_m_code 104 # -temperature_control.hotend2.set_and_wait_m_code 109 # -temperature_control.hotend2.designator T1 # +temperature_control.hotend2.thermistor_pin adc.7 # Pin for the thermistor to read +temperature_control.hotend2.heater_pin 7.7 # Pin that controls the heater +temperature_control.hotend2.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5 +#temperature_control.hotend2.beta 4066 # or set the beta value +temperature_control.hotend2.set_m_code 104 # +temperature_control.hotend2.set_and_wait_m_code 109 # +temperature_control.hotend2.designator T1 # #temperature_control.hotend2.p_factor 13.7 # permanently set the PID values after an auto pid #temperature_control.hotend2.i_factor 0.097 # #temperature_control.hotend2.d_factor 24 # -#temperature_control.hotend2.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v. +#temperature_control.hotend2.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v. -temperature_control.bed.enable false # -temperature_control.bed.thermistor_pin adc.2 # +temperature_control.bed.enable false # +temperature_control.bed.thermistor_pin adc.2 # temperature_control.bed.heater_pin 7.7 # temperature_control.bed.thermistor Honeywell100K # see http://smoothieware.org/temperaturecontrol#toc5 #temperature_control.bed.beta 3974 # or set the beta value @@ -143,53 +238,67 @@ temperature_control.bed.designator B # # when using bang bang ## Switch module for fan control -switch.fan.enable true # +switch.fan.enable false # switch.fan.input_on_command M106 # switch.fan.input_off_command M107 # switch.fan.output_pin 4.1 # switch.fan.output_type pwm # pwm output settable with S parameter in the input_on_comand switch.fan.max_pwm 255 # set max pwm for the pin default is 255 -switch.misc.enable true # +switch.misc.enable false # switch.misc.input_on_command M42 # switch.misc.input_off_command M43 # switch.misc.output_pin 7.7 # -switch.misc.output_type pwm # just an on or off pin +switch.misc.output_type pwm # just an on or off pin ## Endstops endstops_enable true # the endstop module is enabled by default and can be disabled here -#corexy_homing false # set to true if homing on a hbot or corexy -alpha_min_endstop 5.3!^ # add a ! to invert if endstop is NO connected to ground -alpha_max_endstop 1.8^ # NOTE set to nc if this is not installed -alpha_homing_direction home_to_min # or set to home_to_max and set alpha_max -alpha_min 0 # this gets loaded after homing when home_to_min is set -alpha_max 200 # this gets loaded after homing when home_to_max is set -beta_min_endstop 9.6!^ # -beta_max_endstop 9.5^ # -beta_homing_direction home_to_min # -beta_min 0 # -beta_max 200 # -gamma_min_endstop 2.13!^ # -gamma_max_endstop 2.12^ # -gamma_homing_direction home_to_min # -gamma_min 0 # -gamma_max 200 # +rdelta_homing true # forces all three axis to home a the same time regardless of + # what is specified in G28 +alpha_min_endstop 7.4!^ # +alpha_max_endstop 7.7^ # add ! to invert pullup if switch is NO to ground +alpha_homing_direction home_to_max # Home up +alpha_max 300 # +beta_min_endstop 7.5!^ # +beta_max_endstop 4.3^ # +beta_homing_direction home_to_max # +beta_max 300 # +gamma_min_endstop 4.1!^ # +gamma_max_endstop 4.4^ # +gamma_homing_direction home_to_max # +gamma_max 300 # # optional enable limit switches, actions will stop if any enabled limit switch is triggered -#alpha_limit_enable false # set to true to enable X min and max limit switches -#beta_limit_enable false # set to true to enable Y min and max limit switches -#gamma_limit_enable false # set to true to enable Z min and max limit switches +alpha_limit_enable false # set to true to enable X min and max limit switches +beta_limit_enable false # set to true to enable Y min and max limit switches +gamma_limit_enable false # set to true to enable Z min and max limit switches -alpha_fast_homing_rate_mm_s 500 # feedrates in mm/second -beta_fast_homing_rate_mm_s 500 # " -gamma_fast_homing_rate_mm_s 40 # " -alpha_slow_homing_rate_mm_s 250 # " -beta_slow_homing_rate_mm_s 250 # " -gamma_slow_homing_rate_mm_s 20 # " +alpha_fast_homing_rate_mm_s 500 # feedrates in mm/second +beta_fast_homing_rate_mm_s 500 # " +gamma_fast_homing_rate_mm_s 500 # " +alpha_slow_homing_rate_mm_s 20 # " +beta_slow_homing_rate_mm_s 20 # " +gamma_slow_homing_rate_mm_s 20 # " alpha_homing_retract_mm 5 # distance in mm beta_homing_retract_mm 5 # " -gamma_homing_retract_mm 1 # " +gamma_homing_retract_mm 5 # " + +endstop_debounce_count 10 # uncomment if you get noise on your endstops, default is 100 + +alpha_trim 0 # software trim for alpha stepper endstop (in mm) +beta_trim 0 # software trim for beta stepper endstop (in mm) +gamma_trim 0 # software trim for gamma stepper endstop (in mm) + +# optional Z probe +zprobe.enable true # set to true to enable a zprobe +#zprobe.probe_pin 4.4!^ # pin probe is attached to if NC remove the ! +zprobe.slow_feedrate 5 # mm/sec probe feed rate +#zprobe.debounce_count 100 # set if noisy +zprobe.fast_feedrate 100 # move feedrate mm/sec +zprobe.probe_height 15 # how much above bed to start probe -#endstop_debounce_count 100 # uncomment if you get noise on your endstops, default is 100 +# DistanceProbes +distanceprobes_enable true +rdelta_homing true # forces all three axis to home a the same time regardless of diff --git a/src/libs/Pin.cpp b/src/libs/Pin.cpp index 593cab4b..c3fe78bb 100644 --- a/src/libs/Pin.cpp +++ b/src/libs/Pin.cpp @@ -18,9 +18,6 @@ Pin::Pin(){ // Make a new pin object from a string Pin* Pin::from_string(std::string value){ - // Port and pin for when we find them - int port_number; - int pin; // cs is the current position in the string const char* cs = value.c_str(); @@ -50,7 +47,7 @@ Pin* Pin::from_string(std::string value){ // grab first integer as port. pointer to first non-digit goes in cn - port_number = strtol(cs, &cn, 16); //allow hexadecimal portnumbes + port_number = strtol(cs, &cn, 16); //allow hexadecimal port numbers // if cn > cs then strtol read at least one digit if ((cn > cs) && (port_number <= 15)){ @@ -60,18 +57,36 @@ Pin* Pin::from_string(std::string value){ cs = ++cn; // grab pin index. - pin = strtol(cs, &cn, 10); - - map pins = {{(0x00<<8)+0,P0_0},{(0x00<<8)+1,P0_1},{(0x01<<8)+0,P1_0},{(0x01<<8)+1,P1_1},{(0x01<<8)+2,P1_2},{(0x01<<8)+3,P1_3},{(0x01<<8)+4,P1_4},{(0x01<<8)+5,P1_5},{(0x01<<8)+6,P1_6},{(0x01<<8)+7,P1_7},{(0x01<<8)+8,P1_8},{(0x01<<8)+9,P1_9},{(0x01<<8)+10,P1_10},{(0x01<<8)+11,P1_11},{(0x01<<8)+12,P1_12},{(0x01<<8)+13,P1_13},{(0x01<<8)+14,P1_14},{(0x01<<8)+15,P1_15},{(0x01<<8)+16,P1_16},{(0x01<<8)+17,P1_17},{(0x01<<8)+18,P1_18},{(0x01<<8)+20,P1_20},{(0x02<<8)+0,P2_0},{(0x02<<8)+1,P2_1},{(0x02<<8)+2,P2_2},{(0x02<<8)+3,P2_3},{(0x02<<8)+4,P2_4},{(0x02<<8)+5,P2_5},{(0x02<<8)+6,P2_6},{(0x02<<8)+7,P2_7},{(0x02<<8)+8,P2_8},{(0x02<<8)+9,P2_9},{(0x02<<8)+10,P2_10},{(0x02<<8)+11,P2_11},{(0x02<<8)+12,P2_12},{(0x02<<8)+13,P2_13},{(0x03<<8)+1,P3_1},{(0x03<<8)+2,P3_2},{(0x03<<8)+4,P3_4},{(0x03<<8)+5,P3_5},{(0x03<<8)+6,P3_6},{(0x03<<8)+7,P3_7},{(0x03<<8)+8,P3_8},{(0x04<<8)+0,P4_0},{(0x04<<8)+1,P4_1},{(0x04<<8)+2,P4_2},{(0x04<<8)+3,P4_3},{(0x04<<8)+4,P4_4},{(0x04<<8)+5,P4_5},{(0x04<<8)+6,P4_6},{(0x04<<8)+8,P4_8},{(0x04<<8)+9,P4_9},{(0x04<<8)+10,P4_10},{(0x05<<8)+0,P5_0},{(0x05<<8)+1,P5_1},{(0x05<<8)+2,P5_2},{(0x05<<8)+3,P5_3},{(0x05<<8)+4,P5_4},{(0x05<<8)+5,P5_5},{(0x05<<8)+6,P5_6},{(0x05<<8)+7,P5_7},{(0x06<<8)+1,P6_1},{(0x06<<8)+2,P6_2},{(0x06<<8)+3,P6_3},{(0x06<<8)+4,P6_4},{(0x06<<8)+5,P6_5},{(0x06<<8)+6,P6_6},{(0x06<<8)+7,P6_7},{(0x06<<8)+8,P6_8},{(0x06<<8)+9,P6_9},{(0x06<<8)+10,P6_10},{(0x06<<8)+11,P6_11},{(0x06<<8)+12,P6_12},{(0x07<<8)+0,P7_0},{(0x07<<8)+1,P7_1},{(0x07<<8)+2,P7_2},{(0x07<<8)+3,P7_3},{(0x07<<8)+4,P7_4},{(0x07<<8)+5,P7_5},{(0x07<<8)+6,P7_6},{(0x07<<8)+7,P7_7},{(0x08<<8)+8,P8_0},{(0x09<<8)+0,P8_1},{(0x09<<8)+1,P8_2},{(0x09<<8)+2,P8_3},{(0x08<<8)+4,P8_4},{(0x08<<8)+5,P8_5},{(0x08<<8)+6,P8_6},{(0x08<<8)+7,P8_7},{(0x09<<8)+0,P9_0},{(0x09<<8)+1,P9_1},{(0x09<<8)+2,P9_2},{(0x09<<8)+3,P9_3},{(0x09<<8)+4,P9_4},{(0x09<<8)+5,P9_5},{(0x09<<8)+6,P9_6},{(0x0A<<8)+1,PA_1},{(0x0A<<8)+2,PA_2},{(0x0A<<8)+3,PA_3},{(0x0A<<8)+4,PA_4},{(0x0B<<8)+0,PB_0},{(0x0B<<8)+1,PB_1},{(0x0B<<8)+2,PB_2},{(0x0B<<8)+3,PB_3},{(0x0B<<8)+4,PB_4},{(0x0B<<8)+5,PB_5},{(0x0B<<8)+6,PB_6},{(0x0C<<8)+1,PC_1},{(0x0C<<8)+2,PC_2},{(0x0C<<8)+3,PC_3},{(0x0C<<8)+4,PC_4},{(0x0C<<8)+5,PC_5},{(0x0C<<8)+6,PC_6},{(0x0C<<8)+7,PC_7},{(0x0C<<8)+8,PC_8},{(0x0C<<8)+9,PC_9},{(0x0C<<8)+10,PC_10},{(0x0C<<8)+11,PC_11},{(0x0C<<8)+12,PC_12},{(0x0C<<8)+13,PC_13},{(0x0C<<8)+14,PC_14},{(0x0D<<8)+0,PD_0},{(0x0D<<8)+1,PD_1},{(0x0D<<8)+2,PD_2},{(0x0D<<8)+3,PD_3},{(0x0D<<8)+4,PD_4},{(0x0D<<8)+5,PD_5},{(0x0D<<8)+6,PD_6},{(0x0D<<8)+7,PD_7},{(0x0D<<8)+8,PD_8},{(0x0D<<8)+9,PD_9},{(0x0D<<8)+10,PD_10},{(0x0D<<8)+11,PD_11},{(0x0D<<8)+12,PD_12},{(0x0D<<8)+13,PD_13},{(0x0D<<8)+14,PD_14},{(0x0D<<8)+15,PD_15},{(0x0D<<8)+16,PD_16},{(0x0E<<8)+0,PE_0},{(0x0E<<8)+1,PE_1},{(0x0E<<8)+2,PE_2},{(0x0E<<8)+3,PE_3},{(0x0E<<8)+4,PE_4},{(0x0E<<8)+5,PE_5},{(0x0E<<8)+6,PE_6},{(0x0E<<8)+7,PE_7},{(0x0E<<8)+8,PE_8},{(0x0E<<8)+9,PE_9},{(0x0E<<8)+10,PE_10},{(0x0E<<8)+11,PE_11},{(0x0E<<8)+12,PE_12},{(0x0E<<8)+13,PE_13},{(0x0E<<8)+14,PE_14},{(0x0E<<8)+15,PE_15},{(0x0F<<8)+1,PF_1},{(0x0F<<8)+2,PF_2},{(0x0F<<8)+3,PF_3},{(0x0F<<8)+5,PF_5},{(0x0F<<8)+6,PF_6},{(0x0F<<8)+7,PF_7},{(0x0F<<8)+8,PF_8},{(0x0F<<8)+9,PF_9},{(0x0F<<8)+10,PF_10},{(0x0F<<8)+11,PF_11}}; + pin_number = strtol(cs, &cn, 10); +// map pins = {{(0x00<<8)+0,P0_0},{(0x00<<8)+1,P0_1},{(0x01<<8)+0,P1_0},{(0x01<<8)+1,P1_1},{(0x01<<8)+2,P1_2},{(0x01<<8)+3,P1_3},{(0x01<<8)+4,P1_4},{(0x01<<8)+5,P1_5},{(0x01<<8)+6,P1_6},{(0x01<<8)+7,P1_7},{(0x01<<8)+8,P1_8},{(0x01<<8)+9,P1_9},{(0x01<<8)+10,P1_10},{(0x01<<8)+11,P1_11},{(0x01<<8)+12,P1_12},{(0x01<<8)+13,P1_13},{(0x01<<8)+14,P1_14},{(0x01<<8)+15,P1_15},{(0x01<<8)+16,P1_16},{(0x01<<8)+17,P1_17},{(0x01<<8)+18,P1_18},{(0x01<<8)+20,P1_20},{(0x02<<8)+0,P2_0},{(0x02<<8)+1,P2_1},{(0x02<<8)+2,P2_2},{(0x02<<8)+3,P2_3},{(0x02<<8)+4,P2_4},{(0x02<<8)+5,P2_5},{(0x02<<8)+6,P2_6},{(0x02<<8)+7,P2_7},{(0x02<<8)+8,P2_8},{(0x02<<8)+9,P2_9},{(0x02<<8)+10,P2_10},{(0x02<<8)+11,P2_11},{(0x02<<8)+12,P2_12},{(0x02<<8)+13,P2_13},{(0x03<<8)+1,P3_1},{(0x03<<8)+2,P3_2},{(0x03<<8)+4,P3_4},{(0x03<<8)+5,P3_5},{(0x03<<8)+6,P3_6},{(0x03<<8)+7,P3_7},{(0x03<<8)+8,P3_8},{(0x04<<8)+0,P4_0},{(0x04<<8)+1,P4_1},{(0x04<<8)+2,P4_2},{(0x04<<8)+3,P4_3},{(0x04<<8)+4,P4_4},{(0x04<<8)+5,P4_5},{(0x04<<8)+6,P4_6},{(0x04<<8)+8,P4_8},{(0x04<<8)+9,P4_9},{(0x04<<8)+10,P4_10},{(0x05<<8)+0,P5_0},{(0x05<<8)+1,P5_1},{(0x05<<8)+2,P5_2},{(0x05<<8)+3,P5_3},{(0x05<<8)+4,P5_4},{(0x05<<8)+5,P5_5},{(0x05<<8)+6,P5_6},{(0x05<<8)+7,P5_7},{(0x06<<8)+1,P6_1},{(0x06<<8)+2,P6_2},{(0x06<<8)+3,P6_3},{(0x06<<8)+4,P6_4},{(0x06<<8)+5,P6_5},{(0x06<<8)+6,P6_6},{(0x06<<8)+7,P6_7},{(0x06<<8)+8,P6_8},{(0x06<<8)+9,P6_9},{(0x06<<8)+10,P6_10},{(0x06<<8)+11,P6_11},{(0x06<<8)+12,P6_12},{(0x07<<8)+0,P7_0},{(0x07<<8)+1,P7_1},{(0x07<<8)+2,P7_2},{(0x07<<8)+3,P7_3},{(0x07<<8)+4,P7_4},{(0x07<<8)+5,P7_5},{(0x07<<8)+6,P7_6},{(0x07<<8)+7,P7_7},{(0x08<<8)+8,P8_0},{(0x09<<8)+0,P8_1},{(0x09<<8)+1,P8_2},{(0x09<<8)+2,P8_3},{(0x08<<8)+4,P8_4},{(0x08<<8)+5,P8_5},{(0x08<<8)+6,P8_6},{(0x08<<8)+7,P8_7},{(0x09<<8)+0,P9_0},{(0x09<<8)+1,P9_1},{(0x09<<8)+2,P9_2},{(0x09<<8)+3,P9_3},{(0x09<<8)+4,P9_4},{(0x09<<8)+5,P9_5},{(0x09<<8)+6,P9_6},{(0x0A<<8)+1,PA_1},{(0x0A<<8)+2,PA_2},{(0x0A<<8)+3,PA_3},{(0x0A<<8)+4,PA_4},{(0x0B<<8)+0,PB_0},{(0x0B<<8)+1,PB_1},{(0x0B<<8)+2,PB_2},{(0x0B<<8)+3,PB_3},{(0x0B<<8)+4,PB_4},{(0x0B<<8)+5,PB_5},{(0x0B<<8)+6,PB_6},{(0x0C<<8)+1,PC_1},{(0x0C<<8)+2,PC_2},{(0x0C<<8)+3,PC_3},{(0x0C<<8)+4,PC_4},{(0x0C<<8)+5,PC_5},{(0x0C<<8)+6,PC_6},{(0x0C<<8)+7,PC_7},{(0x0C<<8)+8,PC_8},{(0x0C<<8)+9,PC_9},{(0x0C<<8)+10,PC_10},{(0x0C<<8)+11,PC_11},{(0x0C<<8)+12,PC_12},{(0x0C<<8)+13,PC_13},{(0x0C<<8)+14,PC_14},{(0x0D<<8)+0,PD_0},{(0x0D<<8)+1,PD_1},{(0x0D<<8)+2,PD_2},{(0x0D<<8)+3,PD_3},{(0x0D<<8)+4,PD_4},{(0x0D<<8)+5,PD_5},{(0x0D<<8)+6,PD_6},{(0x0D<<8)+7,PD_7},{(0x0D<<8)+8,PD_8},{(0x0D<<8)+9,PD_9},{(0x0D<<8)+10,PD_10},{(0x0D<<8)+11,PD_11},{(0x0D<<8)+12,PD_12},{(0x0D<<8)+13,PD_13},{(0x0D<<8)+14,PD_14},{(0x0D<<8)+15,PD_15},{(0x0D<<8)+16,PD_16},{(0x0E<<8)+0,PE_0},{(0x0E<<8)+1,PE_1},{(0x0E<<8)+2,PE_2},{(0x0E<<8)+3,PE_3},{(0x0E<<8)+4,PE_4},{(0x0E<<8)+5,PE_5},{(0x0E<<8)+6,PE_6},{(0x0E<<8)+7,PE_7},{(0x0E<<8)+8,PE_8},{(0x0E<<8)+9,PE_9},{(0x0E<<8)+10,PE_10},{(0x0E<<8)+11,PE_11},{(0x0E<<8)+12,PE_12},{(0x0E<<8)+13,PE_13},{(0x0E<<8)+14,PE_14},{(0x0E<<8)+15,PE_15},{(0x0F<<8)+1,PF_1},{(0x0F<<8)+2,PF_2},{(0x0F<<8)+3,PF_3},{(0x0F<<8)+5,PF_5},{(0x0F<<8)+6,PF_6},{(0x0F<<8)+7,PF_7},{(0x0F<<8)+8,PF_8},{(0x0F<<8)+9,PF_9},{(0x0F<<8)+10,PF_10},{(0x0F<<8)+11,PF_11}}; + map pins = { + {(0x00<<8)+0,P0_0},{(0x00<<8)+1,P0_1}, + {(0x01<<8)+0,P1_0},{(0x01<<8)+1,P1_1},{(0x01<<8)+2,P1_2},{(0x01<<8)+3,P1_3},{(0x01<<8)+4,P1_4},{(0x01<<8)+5,P1_5},{(0x01<<8)+6,P1_6},{(0x01<<8)+7,P1_7},{(0x01<<8)+8,P1_8},{(0x01<<8)+9,P1_9},{(0x01<<8)+10,P1_10},{(0x01<<8)+11,P1_11},{(0x01<<8)+12,P1_12},{(0x01<<8)+13,P1_13},{(0x01<<8)+14,P1_14},{(0x01<<8)+15,P1_15},{(0x01<<8)+16,P1_16},{(0x01<<8)+17,P1_17},{(0x01<<8)+18,P1_18},{(0x01<<8)+19,P1_19},{(0x01<<8)+20,P1_20}, + {(0x02<<8)+0,P2_0},{(0x02<<8)+1,P2_1},{(0x02<<8)+2,P2_2},{(0x02<<8)+3,P2_3},{(0x02<<8)+4,P2_4},{(0x02<<8)+5,P2_5},{(0x02<<8)+6,P2_6},{(0x02<<8)+7,P2_7},{(0x02<<8)+8,P2_8},{(0x02<<8)+9,P2_9},{(0x02<<8)+10,P2_10},{(0x02<<8)+11,P2_11},{(0x02<<8)+12,P2_12},{(0x02<<8)+13,P2_13}, + {(0x03<<8)+0,P3_0},{(0x03<<8)+1,P3_1},{(0x03<<8)+2,P3_2}, {(0x03<<8)+4,P3_4},{(0x03<<8)+5,P3_5},{(0x03<<8)+6,P3_6},{(0x03<<8)+7,P3_7},{(0x03<<8)+8,P3_8}, + {(0x04<<8)+0,P4_0},{(0x04<<8)+1,P4_1},{(0x04<<8)+2,P4_2},{(0x04<<8)+3,P4_3},{(0x04<<8)+4,P4_4},{(0x04<<8)+5,P4_5},{(0x04<<8)+6,P4_6}, {(0x04<<8)+8,P4_8},{(0x04<<8)+9,P4_9},{(0x04<<8)+10,P4_10}, + {(0x05<<8)+0,P5_0},{(0x05<<8)+1,P5_1},{(0x05<<8)+2,P5_2},{(0x05<<8)+3,P5_3},{(0x05<<8)+4,P5_4},{(0x05<<8)+5,P5_5},{(0x05<<8)+6,P5_6},{(0x05<<8)+7,P5_7}, + {(0x06<<8)+1,P6_1},{(0x06<<8)+2,P6_2},{(0x06<<8)+3,P6_3},{(0x06<<8)+4,P6_4},{(0x06<<8)+5,P6_5},{(0x06<<8)+6,P6_6},{(0x06<<8)+7,P6_7},{(0x06<<8)+8,P6_8},{(0x06<<8)+9,P6_9},{(0x06<<8)+10,P6_10},{(0x06<<8)+11,P6_11},{(0x06<<8)+12,P6_12}, + {(0x07<<8)+0,P7_0},{(0x07<<8)+1,P7_1},{(0x07<<8)+2,P7_2},{(0x07<<8)+3,P7_3},{(0x07<<8)+4,P7_4},{(0x07<<8)+5,P7_5},{(0x07<<8)+6,P7_6},{(0x07<<8)+7,P7_7}, + {(0x08<<8)+0,P8_0},{(0x09<<8)+1,P8_1},{(0x08<<8)+2,P8_2},{(0x09<<8)+3,P8_3},{(0x08<<8)+4,P8_4},{(0x08<<8)+5,P8_5},{(0x08<<8)+6,P8_6},{(0x08<<8)+7,P8_7}, + {(0x09<<8)+0,P9_0},{(0x09<<8)+1,P9_1},{(0x09<<8)+2,P9_2},{(0x09<<8)+3,P9_3},{(0x09<<8)+4,P9_4},{(0x09<<8)+5,P9_5},{(0x09<<8)+6,P9_6}, + {(0x0A<<8)+1,PA_1},{(0x0A<<8)+2,PA_2},{(0x0A<<8)+3,PA_3},{(0x0A<<8)+4,PA_4}, + {(0x0B<<8)+0,PB_0},{(0x0B<<8)+1,PB_1},{(0x0B<<8)+2,PB_2},{(0x0B<<8)+3,PB_3},{(0x0B<<8)+4,PB_4},{(0x0B<<8)+5,PB_5},{(0x0B<<8)+6,PB_6}, + {(0x0C<<8)+1,PC_1},{(0x0C<<8)+2,PC_2},{(0x0C<<8)+3,PC_3},{(0x0C<<8)+4,PC_4},{(0x0C<<8)+5,PC_5},{(0x0C<<8)+6,PC_6},{(0x0C<<8)+7,PC_7},{(0x0C<<8)+8,PC_8},{(0x0C<<8)+9,PC_9},{(0x0C<<8)+10,PC_10},{(0x0C<<8)+11,PC_11},{(0x0C<<8)+12,PC_12},{(0x0C<<8)+13,PC_13},{(0x0C<<8)+14,PC_14}, + {(0x0D<<8)+0,PD_0},{(0x0D<<8)+1,PD_1},{(0x0D<<8)+2,PD_2},{(0x0D<<8)+3,PD_3},{(0x0D<<8)+4,PD_4},{(0x0D<<8)+5,PD_5},{(0x0D<<8)+6,PD_6},{(0x0D<<8)+7,PD_7},{(0x0D<<8)+8,PD_8},{(0x0D<<8)+9,PD_9},{(0x0D<<8)+10,PD_10},{(0x0D<<8)+11,PD_11},{(0x0D<<8)+12,PD_12},{(0x0D<<8)+13,PD_13},{(0x0D<<8)+14,PD_14},{(0x0D<<8)+15,PD_15},{(0x0D<<8)+16,PD_16}, + {(0x0E<<8)+0,PE_0},{(0x0E<<8)+1,PE_1},{(0x0E<<8)+2,PE_2},{(0x0E<<8)+3,PE_3},{(0x0E<<8)+4,PE_4},{(0x0E<<8)+5,PE_5},{(0x0E<<8)+6,PE_6},{(0x0E<<8)+7,PE_7},{(0x0E<<8)+8,PE_8},{(0x0E<<8)+9,PE_9},{(0x0E<<8)+10,PE_10},{(0x0E<<8)+11,PE_11},{(0x0E<<8)+12,PE_12},{(0x0E<<8)+13,PE_13},{(0x0E<<8)+14,PE_14},{(0x0E<<8)+15,PE_15}, + {(0x0F<<8)+1,PF_1},{(0x0F<<8)+2,PF_2},{(0x0F<<8)+3,PF_3},{(0x0F<<8)+4,PF_4},{(0x0F<<8)+5,PF_5},{(0x0F<<8)+6,PF_6},{(0x0F<<8)+7,PF_7},{(0x0F<<8)+8,PF_8},{(0x0F<<8)+9,PF_9},{(0x0F<<8)+10,PF_10},{(0x0F<<8)+11,PF_11} + }; + // Find the pin - if( pins.count((port_number<<8)+pin) > 0 ){ - this->mbed_pin = new DigitalInOut( pins[(port_number<<8)+pin] ); - this->pinName = pins[(port_number<<8)+pin]; + if( pins.count((port_number<<8)+pin_number) > 0 ){ + this->mbed_pin = new DigitalInOut( pins[(port_number<<8)+pin_number] ); + this->pinName = pins[(port_number<<8)+pin_number]; } // if strtol read some numbers, cn will point to the first non-digit - if ((cn > cs) && (pin < 32)){ + if ((cn > cs) && (pin_number < 32)){ // now check for modifiers:- // ! = invert pin @@ -163,18 +178,17 @@ mbed::PwmOut* Pin::hardware_pwm() mbed::InterruptIn* Pin::interrupt_pin() { if(!this->valid) return nullptr; -/* + // set as input as_input(); - if (port_number == 0 || port_number == 2) { - PinName pinname = port_pin((PortName)port_number, pin); + if (port_number >= 0 && port_number <= 7 && pin_number >= 0 && pin_number <= 7) { //LPC4330 allows bits 0..7 on ports 0..7 + PinName pinname = port_pin((PortName)port_number, pin_number); return new mbed::InterruptIn(pinname); - }else{ this->valid= false; return nullptr; } -*/ + return nullptr; } diff --git a/src/libs/Pin.h b/src/libs/Pin.h index 49ca89e0..8938ddd7 100644 --- a/src/libs/Pin.h +++ b/src/libs/Pin.h @@ -26,7 +26,7 @@ class Pin { } inline bool equals(const Pin& other) const { - //return (this->pin == other.pin) && (this->port == other.port); + //return (this->pin == other.pin) && (this->port == other.port); //TODO this needs to be fixed return false; } @@ -87,12 +87,12 @@ class Pin { //getter to access to mbed pinName PinName getPinName() {return pinName; } + int port_number; + int pin_number; + private: //Added pinName PinName pinName; }; - - - #endif diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 87685e8d..9d3653c5 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -16,6 +16,8 @@ //#include "system_LPC17xx.h" // mbed.h lib #include #include +#include "SEGGER_SYSVIEW.h" +#include "SEGGER_RTT_Conf.h" #ifdef STEPTICKER_DEBUG_PIN #include "gpio.h" @@ -154,30 +156,38 @@ inline void StepTicker::unstep_tick(){ // Acceleration timer interrupt handler extern "C" void RITIMER_IRQHandler (void) { + LPC_RITIMER->CTRL |= 1L; //LPC_TIMER2->IR |= 1 << 0; StepTicker::global_step_ticker->acceleration_tick(); //NVIC_ClearPendingIRQ(TIMER2_IRQn); LPC_RITIMER->COUNTER = 0; + SEGGER_SYSVIEW_RecordExitISR(); } // Unstep timer interrupt handler extern "C" void TIMER1_IRQHandler (void) { +// LPC_TIMER1->IR |= 1 << 0; StepTicker::global_step_ticker->unstep_tick(); NVIC_ClearPendingIRQ(TIMER1_IRQn); +// SEGGER_SYSVIEW_RecordExitISR(); } // Step timer interrupt handler extern "C" void TIMER0_IRQHandler (void) { +// SEGGER_RTT_LOCK(); +// SEGGER_SYSVIEW_RecordEnterISR(); LPC_TIMER0->IR |= 1 << 0; StepTicker::global_step_ticker->step_tick(); NVIC_ClearPendingIRQ(TIMER0_IRQn); +// SEGGER_SYSVIEW_RecordExitISR(); +// SEGGER_RTT_UNLOCK(); } -// RIT Timeer interrupt handler +// RIT Timer interrupt handler void StepTicker::acceleration_tick() { // call registered acceleration handlers for (size_t i = 0; i < acceleration_tick_handlers.size(); ++i) { diff --git a/src/libs/StepperMotor.cpp b/src/libs/StepperMotor.cpp index 87b89f1f..98d19b57 100644 --- a/src/libs/StepperMotor.cpp +++ b/src/libs/StepperMotor.cpp @@ -13,9 +13,6 @@ #include #include "mbed.h" -// in steps/sec the default minimum speed (was 20steps/sec hardcoded) -float StepperMotor::default_minimum_actuator_rate= 20.0F; - // A StepperMotor represents an actual stepper motor. It is used to generate steps that move the actual motor at a given speed StepperMotor::StepperMotor() @@ -50,7 +47,6 @@ void StepperMotor::init() steps_per_mm = 1.0F; max_rate = 50.0F; - minimum_step_rate = default_minimum_actuator_rate; last_milestone_steps = 0; last_milestone_mm = 0.0F; @@ -126,7 +122,6 @@ void StepperMotor::signal_move_finished() // work is done ! 8t this->moving = false; this->steps_to_move = 0; - this->minimum_step_rate = default_minimum_actuator_rate; // signal it to whatever cares // in this call a new block may start, new moves set and new speeds diff --git a/src/libs/StepperMotor.h b/src/libs/StepperMotor.h index 0f1ede63..87f0ab92 100644 --- a/src/libs/StepperMotor.h +++ b/src/libs/StepperMotor.h @@ -78,7 +78,6 @@ class StepperMotor { float steps_per_mm; float max_rate; // this is not really rate it is in mm/sec, misnamed used in Robot and Extruder float minimum_step_rate; // this is the minimum step_rate in steps/sec for this motor for this block - static float default_minimum_actuator_rate; volatile int32_t current_position_steps; int32_t last_milestone_steps; @@ -119,4 +118,3 @@ class StepperMotor { }; #endif - diff --git a/src/libs/checksumm.h b/src/libs/checksumm.h index 16aab8fa..3916b8d3 100644 --- a/src/libs/checksumm.h +++ b/src/libs/checksumm.h @@ -51,7 +51,9 @@ constexpr uint16_t operator "" _checksum(const char* s, size_t n) { #define CHECKSUM(X) std::integral_constant::value #else /* !CHECKSUM_USE_CPP */ - +#ifdef __CDT_PARSER__ +#define CHECKSUM(X) ((uint16_t)sizeof(X)) +#else /* Adam Green's old and crusty C approach. */ /* Recursively define SUM1, the basic checksum % 255 */ #define SUM1_1(X) ((X)[0] % 255) @@ -199,6 +201,7 @@ constexpr uint16_t operator "" _checksum(const char* s, size_t n) { sizeof(X) == 33 ? CHECKSUM_32(X) : \ 0xFFFF) #endif /* DEBUG */ +#endif /* __CDT_PARSER__ */ #endif /* CHECKSUM_USE_CPP */ #endif /* _CHECKSUM_MACRO_H_ */ diff --git a/src/libs/nuts_bolts.h b/src/libs/nuts_bolts.h index af52b52f..f17f4e99 100644 --- a/src/libs/nuts_bolts.h +++ b/src/libs/nuts_bolts.h @@ -20,10 +20,14 @@ along with Grbl. If not, see . #define X_AXIS 0 #define Y_AXIS 1 #define Z_AXIS 2 +#define E_AXIS 3 #define ALPHA_STEPPER 0 #define BETA_STEPPER 1 #define GAMMA_STEPPER 2 +#define DELTA_STEPPER 3 +#define EPSILON_STEPPER 4 +#define ZETA_STEPPER 5 #define clear_vector(a) memset(a, 0, sizeof(a)) #define clear_vector_float(a) memset(a, 0, sizeof(a)) diff --git a/src/libs/utils.h b/src/libs/utils.h index 4cb3025e..f40c20c4 100644 --- a/src/libs/utils.h +++ b/src/libs/utils.h @@ -39,4 +39,6 @@ int append_parameters(char *buf, std::vector> params, size string wcs2gcode(int wcs); void safe_delay(uint32_t delay); +#define confine(value, min, max) (((value) < (min))?(min):(((value) > (max))?(max):(value))) + #endif diff --git a/src/main.cpp b/src/main.cpp index 4ab34e46..6e70e8dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,10 +5,13 @@ #include "Kernel.h" #include "SwitchPool.h" #include "TemperatureControlPool.h" +//#include "RotaryDeltaCalibration.h" #include "Endstops.h" #include "Laser.h" #include "Config.h" #include "StreamOutputPool.h" +#include "MotorDriverControl.h" +#include "SEGGER_SYSVIEW.h" DigitalOut leds[4] = { DigitalOut(LED1), @@ -18,7 +21,7 @@ DigitalOut leds[4] = { }; int main() { - + SEGGER_SYSVIEW_Conf(); int cnt = 0; // Kernel creates modules, and receives and dispatches events between them @@ -30,6 +33,9 @@ int main() { // Create and add main modules kernel->add_module( new Endstops() ); kernel->add_module( new Laser() ); + //kernel->add_module( new ZProbe() ); + //kernel->add_module( new RotaryDeltaCalibration() ); + kernel->add_module( new MotorDriverControl(0) ); // Create all Switch modules SwitchPool *sp= new SwitchPool(); diff --git a/src/makefile b/src/makefile index a7d0b87f..e25a6c00 100644 --- a/src/makefile +++ b/src/makefile @@ -25,6 +25,8 @@ NO_FLOAT_SCANF := 1 NO_FLOAT_PRINTF := 0 LIBS_PREFIX := configdefault.o MRI_SEMIHOST_STDIO := 0 +MBED_LIBS += USBHost +#MBED_LIBS += USBDevice # Use C++11 features for the checksums DEFINES += -DCHECKSUM_USE_CPP @@ -78,7 +80,13 @@ GCC4MBED_TYPE := Checked else GCC4MBED_TYPE := Release endif - + +# generate a git version string +ifneq "$(OS)" "Windows_NT" +DEFINES += -D__GITVERSIONSTRING__=\"$(shell ./generate-version.sh)\" +else +DEFINES += -D__GITVERSIONSTRING__=\"$(shell generate-version.bat)\" +endif include $(GCC4MBED_DIR)/build/gcc4mbed.mk diff --git a/src/modules/robot/Planner.cpp b/src/modules/robot/Planner.cpp index a7525d5d..371d6774 100644 --- a/src/modules/robot/Planner.cpp +++ b/src/modules/robot/Planner.cpp @@ -5,7 +5,6 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -using namespace std; #include #include "mri.h" @@ -26,6 +25,8 @@ using namespace std; #include +using namespace std; + #define acceleration_checksum CHECKSUM("acceleration") #define z_acceleration_checksum CHECKSUM("z_acceleration") #define junction_deviation_checksum CHECKSUM("junction_deviation") @@ -98,7 +99,6 @@ void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, block->millimeters = distance; // Calculate speed in mm/sec for each axis. No divide by zero due to previous checks. - // NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c if( distance > 0.0F ) { block->nominal_speed = rate_mm_s; // (mm/s) Always > 0 block->nominal_rate = ceilf(block->steps_event_count * rate_mm_s / distance); // (step/s) Always > 0 diff --git a/src/modules/robot/Robot.cpp b/src/modules/robot/Robot.cpp index 399544d1..d09f0d0d 100644 --- a/src/modules/robot/Robot.cpp +++ b/src/modules/robot/Robot.cpp @@ -37,7 +37,7 @@ using std::string; #include "ConfigValue.h" #include "libs/StreamOutput.h" #include "StreamOutputPool.h" -// TOADDBACKĀ #include "ExtruderPublicAccess.h" +#include "ExtruderPublicAccess.h" #include "GcodeDispatch.h" @@ -559,9 +559,6 @@ void Robot::on_gcode_received(void *argument) mps = 0.0F; THEKERNEL->planner->minimum_planner_speed = mps; } - if (gcode->has_letter('Y')) { - actuators[0]->default_minimum_actuator_rate = gcode->get_value('Y'); - } break; case 220: // M220 - speed override percentage diff --git a/src/modules/robot/Robot.h b/src/modules/robot/Robot.h index 0d194590..16a8d1b0 100644 --- a/src/modules/robot/Robot.h +++ b/src/modules/robot/Robot.h @@ -17,6 +17,8 @@ using std::string; #include "libs/Module.h" #include "ActuatorCoordinates.h" +#include "Pin.h" +#include class Gcode; class BaseSolution; @@ -118,6 +120,16 @@ class Robot : public Module { int arc_correction; // Setting : how often to rectify arc computation float max_speeds[3]; // Setting : max allowable speed in mm/m for each axis + //used to initialize the TMC2130 stepper motor driver if present + mbed::SPI *spi; + Pin spi_sck_pin; + Pin spi_mosi_pin; + Pin spi_miso_pin; + //Note the spi_cs pin is read per stepper from the config file, for example for a Bamabino210[E] board: + //alpha_spi_cs_pin 7.4 # Pin for alpha SilentStepStick SPI Enable pin + //beta_spi_cs_pin 7.5 # Pin for beta SilentStepStick SPI Enable pin + //gamma_spi_cs_pin 4.1 # Pin for gamma SilentStepStick SPI Enable pin + // Used by Stepper, Planner friend class Planner; friend class Stepper; diff --git a/src/modules/robot/Stepper.cpp b/src/modules/robot/Stepper.cpp index fd201c36..ad174674 100644 --- a/src/modules/robot/Stepper.cpp +++ b/src/modules/robot/Stepper.cpp @@ -34,8 +34,10 @@ using namespace std; Stepper::Stepper() { - this->current_block = NULL; + this->current_block = nullptr; this->force_speed_update = false; + trapezoid_adjusted_rate = 0; + main_stepper = nullptr; } //Called when the module has just been loaded @@ -50,7 +52,7 @@ void Stepper::on_module_loaded() this->on_config_reload(this); // Acceleration ticker - THEKERNEL->step_ticker->register_acceleration_tick_handler([this](){trapezoid_generator_tick(); }); + THEKERNEL->step_ticker->register_acceleration_tick_handler([this]{trapezoid_generator_tick(); }); // Attach to the end_of_move stepper event for (auto actuator : THEKERNEL->robot->actuators) diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp index 07aa6074..284d1a09 100644 --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp @@ -22,7 +22,6 @@ #define delta_mirror_xy_checksum CHECKSUM("delta_mirror_xy") const static float pi = 3.14159265358979323846; // PI -const static float two_pi = 2 * pi; const static float sin120 = 0.86602540378443864676372317075294; //sqrt3/2.0 const static float cos120 = -0.5; const static float tan60 = 1.7320508075688772935274463415059; //sqrt3; diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index 6903030f..cec39acf 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -30,6 +30,7 @@ #include "StepTicker.h" #include "BaseSolution.h" #include "SerialMessage.h" +#include "SEGGER_SYSVIEW.h" #include @@ -225,7 +226,7 @@ void Endstops::load_config() this->limit_enable[Y_AXIS] = THEKERNEL->config->value(beta_limit_enable_checksum)->by_default(false)->as_bool(); this->limit_enable[Z_AXIS] = THEKERNEL->config->value(gamma_limit_enable_checksum)->by_default(false)->as_bool(); - // set to true by default for deltas duwe to trim, false on cartesians + // set to true by default for deltas due to trim, false on cartesians this->move_to_origin_after_home = THEKERNEL->config->value(move_to_origin_checksum)->by_default(is_delta)->as_bool(); if(this->limit_enable[X_AXIS] || this->limit_enable[Y_AXIS] || this->limit_enable[Z_AXIS]) { @@ -320,7 +321,7 @@ void Endstops::back_off_home(char axes_to_move) this->status = BACK_OFF_HOME; // these are handled differently - if(is_delta) { + if(this->is_delta || this->is_rdelta) { //TODO changed from is_delta // Move off of the endstop using a regular relative move in Z only params.push_back({'Z', this->retract_mm[Z_AXIS] * (this->home_direction[Z_AXIS] ? 1 : -1)}); @@ -384,6 +385,7 @@ bool Endstops::wait_for_homed(char axes_to_move) bool running = true; unsigned int debounce[3] = {0, 0, 0}; while (running) { + SEGGER_SYSVIEW_Print("Endstop::wait_for_homed\n"); running = false; THEKERNEL->call_event(ON_IDLE); @@ -392,22 +394,31 @@ bool Endstops::wait_for_homed(char axes_to_move) for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { if ( ( axes_to_move >> c ) & 1 ) { + //SEGGER_SYSVIEW_PrintfHost(" debounce[%u] = %u\n",c,debounce[c]); if ( this->pins[c + (this->home_direction[c] ? 0 : 3)].get() ) { + //SEGGER_SYSVIEW_PrintfHost("A debounce[%u] = %u\n",c,debounce[c]); if ( debounce[c] < debounce_count ) { debounce[c]++; running = true; } else if ( STEPPER[c]->is_moving() ) { - STEPPER[c]->move(0, 0); + SEGGER_SYSVIEW_PrintfHost("Reached endstop for %u\n",c); + //STEPPER[c]->move(0, 0); + STEPPER[c]->force_finish_move(); //TODO test + SEGGER_SYSVIEW_PrintfHost("Axis to move a %u\n",axes_to_move); axes_to_move &= ~(1 << c); // no need to check it again + SEGGER_SYSVIEW_PrintfHost("Axis to move %u\n",axes_to_move); } } else { // The endstop was not hit yet + //SEGGER_SYSVIEW_PrintfHost("C debounce[%u] = %u\n",c,debounce[c]); running = true; debounce[c] = 0; } } } + SEGGER_SYSVIEW_PrintfHost("%u,%u,%u\n",debounce[0],debounce[1],debounce[2]); } + SEGGER_SYSVIEW_Print("Endstop::wait_for_homed EXIT\n"); return true; } diff --git a/src/modules/tools/temperaturecontrol/max31855.cpp b/src/modules/tools/temperaturecontrol/max31855.cpp index bd4fe315..6fcb488b 100644 --- a/src/modules/tools/temperaturecontrol/max31855.cpp +++ b/src/modules/tools/temperaturecontrol/max31855.cpp @@ -32,7 +32,7 @@ Max31855::~Max31855() // Get configuration from the config file void Max31855::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) { - /* TOADDBACK + // Chip select this->spi_cs_pin.from_string(THEKERNEL->config->value(module_checksum, name_checksum, chip_select_checksum)->by_default("0.16")->as_string()); this->spi_cs_pin.set(true); @@ -45,18 +45,18 @@ void Max31855::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) PinName sclk; if(spi_channel == 0) { // Channel 0 - mosi=P0_18; miso=P0_17; sclk=P0_15; + mosi=P1_2; miso=P1_1; sclk=P3_0; } else { // Channel 1 - mosi=P0_9; miso=P0_8; sclk=P0_7; + mosi=P1_4; miso=P1_3; sclk=PF_4; } delete spi; spi = new mbed::SPI(mosi, miso, sclk); // Spi settings: 1MHz (default), 16 bits, mode 0 (default) - spi->format(16); - */ + spi->format(16); //TODO the robot.cpp uses spi at 8 bits for the tmc2130 stepper motor in StepperMotor.cpp so we need to perhaps change this to 8 bits + } float Max31855::get_temperature() From e718ae337ef70be200d959592f5df1894d1f637c Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 22 Oct 2016 17:07:39 +1300 Subject: [PATCH 03/66] modified: README.md --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0dbad548..8ce377e7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,15 @@ # Smoothie2 -A Smoothie firmware port to the Smoothieboard v2 boards. +A Smoothie firmware port to the Bambino210E board ahead of making changes to support the SmoothieV2 hardware. -Current status : Gcode processing, motion planning, and step generation work. Many secondary features need porting, and some major lower level things need implementation ( USB, Ethernet, SDIO ). +There are no guarantees that this code works, won't burn down your house or destroy your electronics or hardware, use entirely at your own risk. + +There are a lot of bugs in the code and it is very much a work in progress, the reason it is published is to allow others to contriute before it is folded back into the Smoothie2 main repo. + +Current status : +* This code base is equivalent to Smoothie V1 as at 2016-10-18. +* The Extruder code is being worked on and as of 2016-10-22 and does not yet compile. +* Some major lower level things need implementation ( USB, Ethernet, SDIO ). # Compiling @@ -27,17 +34,14 @@ And finally compile the code make # Board Specific Programming/Debugging Notes -* [Smoothie2 Pro Proto1 Boards](https://github.com/Smoothieware/Smoothie2/blob/master/notes/Smoothie2Proto1-board.creole#smoothie2-board-programmingdebugging-notes) * [Bambino210E Boards](https://github.com/Smoothieware/Smoothie2/blob/master/notes/Bambino210E-board.creole#bambino210e-board-programmingdebugging-notes) # TODO :Ā  Current major TODOs :Ā  - * Porting of existing functionality that hasn't been ported yet ( anything in https://github.com/Smoothieware/Smoothieware/tree/edge/src/modules that isn't ported yet ) - * Some modules have been ported, but the underlying low-level/HAL stuff to talk to the peripherals isn't ( like SPI, or ADCs etc ), it's just dummy objects right now. So that needs porting too. - * USB and Ethernet need to be implemented. - * Doing the step generation on the M0 co-processor instead of on the M4 main core. - * This port was forked from Smoothie months ago, it should be updated to incoprorate changes to Smoothie done in the meantime. + * Complete the Extruder port (IT DOES NOT YET COMPILE, STILL AN ALPHA PORT) + * SD-Card, USB and Ethernet need to be implemented. + * Move the step generation to the M0 co-processor instead of on the M4 main core. TODOĀ functionality to port from v1 in more detail : * libs/Adc.cpp : Port low level ADC functionality, then re-enable it in TemperatureControl ( note :Ā there is some weird pin configuration surrounding this that makes it more complicated than it seems, but I don't remember what it is. Logxen@gmail.com probably remembers what it is ). @@ -49,15 +53,11 @@ TODOĀ functionality to port from v1 in more detail : * libs/Watchdog : Port ( no mBed libraries available that I could find ) * libs/Hook.h : Uses doubles instead of ints ( in v1 ) for some reason, should be fixed * modules/tools/drillingcycles : Port ( should be fairly simple ) - * modules/tools/extruder :Ā Port + * modules/tools/extruder :Ā Port Under way * modules/tools/filamentdetector : Port * modules/tools/filamentdetector : Get PWM to work - * modules/tools/scaracal : Port - * modules/tools/spindle :Ā Port and get PWM to work + * modules/tools/spindle :Ā Get PWM to work * modules/tools/temperaturecontrol : Finish porting, get the ADC to actually work, port the AD8495 temperature input - * modules/tools/temperatureswitch : Port ( should be fairly easy ) - * modules/tools/toolmanager :Ā Port ( should be trivial, is needed for Extruder ) - * modules/tools/touchprobe :Ā Port ( could be quite easy ) * modules/tools/zprobe :Ā Port ( could be quite easy ) * modules/tools/simpleshell :Ā Port From 92da6c4b0646b497caa1fc400b46be27f00a3c60 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 22 Oct 2016 22:59:43 +1300 Subject: [PATCH 04/66] modified: src/modules/robot/Stepper.cpp --- src/modules/robot/Stepper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/robot/Stepper.cpp b/src/modules/robot/Stepper.cpp index ad174674..243311f1 100644 --- a/src/modules/robot/Stepper.cpp +++ b/src/modules/robot/Stepper.cpp @@ -52,7 +52,7 @@ void Stepper::on_module_loaded() this->on_config_reload(this); // Acceleration ticker - THEKERNEL->step_ticker->register_acceleration_tick_handler([this]{trapezoid_generator_tick(); }); + THEKERNEL->step_ticker->register_acceleration_tick_handler([this](){trapezoid_generator_tick(); }); // Attach to the end_of_move stepper event for (auto actuator : THEKERNEL->robot->actuators) From 4ba02f8c211cd3c33ea65c29acfd4604d0bc0d8f Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 22 Oct 2016 23:10:50 +1300 Subject: [PATCH 05/66] modified: src/modules/tools/temperaturecontrol/max31855.cpp --- src/modules/tools/temperaturecontrol/max31855.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/modules/tools/temperaturecontrol/max31855.cpp b/src/modules/tools/temperaturecontrol/max31855.cpp index 6fcb488b..62189686 100644 --- a/src/modules/tools/temperaturecontrol/max31855.cpp +++ b/src/modules/tools/temperaturecontrol/max31855.cpp @@ -43,13 +43,15 @@ void Max31855::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) PinName miso; PinName mosi; PinName sclk; + if(spi_channel == 0) { - // Channel 0 - mosi=P1_2; miso=P1_1; sclk=P3_0; + mosi = SPI0_MOSI; miso = SPI0_MISO; sclk = SPI0_SCK; + } else if(spi_channel == 1) { + mosi = SPI1_MOSI; miso = SPI1_MISO; sclk = SPI1_SCK; } else { - // Channel 1 - mosi=P1_4; miso=P1_3; sclk=PF_4; - } + THEKERNEL->streams->printf("Temperaturecontrol ERROR: Unknown SPI Channel: %d\n", spi_channel); + return; + } delete spi; spi = new mbed::SPI(mosi, miso, sclk); From 57ad4bc3d44361f7029793001f6fd4e4753b3b2c Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 23 Oct 2016 10:53:19 +1300 Subject: [PATCH 06/66] deleted: src/Segger_SystemView/Global.h new file: src/Segger_SystemView/README.md deleted: src/Segger_SystemView/SEGGER.h deleted: src/Segger_SystemView/SEGGER_RTT.c deleted: src/Segger_SystemView/SEGGER_RTT.h deleted: src/Segger_SystemView/SEGGER_RTT_Conf.h deleted: src/Segger_SystemView/SEGGER_SYSVIEW.c deleted: src/Segger_SystemView/SEGGER_SYSVIEW.h deleted: src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h deleted: src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h deleted: src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c deleted: src/Segger_SystemView/SEGGER_SYSVIEW_Int.h --- src/Segger_SystemView/Global.h | 102 - src/Segger_SystemView/README.md | 15 + src/Segger_SystemView/SEGGER.h | 155 - src/Segger_SystemView/SEGGER_RTT.c | 1385 --------- src/Segger_SystemView/SEGGER_RTT.h | 246 -- src/Segger_SystemView/SEGGER_RTT_Conf.h | 269 -- src/Segger_SystemView/SEGGER_SYSVIEW.c | 2763 ----------------- src/Segger_SystemView/SEGGER_SYSVIEW.h | 334 -- src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h | 160 - .../SEGGER_SYSVIEW_ConfDefaults.h | 179 -- .../SEGGER_SYSVIEW_Config_NoOS.c | 115 - src/Segger_SystemView/SEGGER_SYSVIEW_Int.h | 110 - 12 files changed, 15 insertions(+), 5818 deletions(-) delete mode 100755 src/Segger_SystemView/Global.h create mode 100644 src/Segger_SystemView/README.md delete mode 100755 src/Segger_SystemView/SEGGER.h delete mode 100755 src/Segger_SystemView/SEGGER_RTT.c delete mode 100755 src/Segger_SystemView/SEGGER_RTT.h delete mode 100755 src/Segger_SystemView/SEGGER_RTT_Conf.h delete mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW.c delete mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW.h delete mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h delete mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h delete mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c delete mode 100755 src/Segger_SystemView/SEGGER_SYSVIEW_Int.h diff --git a/src/Segger_SystemView/Global.h b/src/Segger_SystemView/Global.h deleted file mode 100755 index e93acbcb..00000000 --- a/src/Segger_SystemView/Global.h +++ /dev/null @@ -1,102 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** ----------------------------------------------------------------------- -File : Global.h -Purpose : Global types - In case your application already has a Global.h, you should - merge the files. In order to use Segger code, the types - U8, U16, U32, I8, I16, I32 need to be defined in Global.h; - additional definitions do not hurt. ----------------------------END-OF-HEADER------------------------------ -*/ - -#ifndef GLOBAL_H // Guard against multiple inclusion -#define GLOBAL_H - -#define U8 unsigned char -#define U16 unsigned short -#define U32 unsigned long -#define I8 signed char -#define I16 signed short -#define I32 signed long - -#ifdef _WIN32 - // - // Microsoft VC6 compiler related - // - #define U64 unsigned __int64 - #define U128 unsigned __int128 - #define I64 __int64 - #define I128 __int128 - #if _MSC_VER <= 1200 - #define U64_C(x) x##UI64 - #else - #define U64_C(x) x##ULL - #endif -#else - // - // C99 compliant compiler - // - #define U64 unsigned long long - #define I64 signed long long - #define U64_C(x) x##ULL -#endif - -#endif // Avoid multiple inclusion - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md new file mode 100644 index 00000000..8c2d9ef6 --- /dev/null +++ b/src/Segger_SystemView/README.md @@ -0,0 +1,15 @@ +#Segger JTrace SystemView debugging and integration + +Go to http://www.segger.com and download their Systemview free tool and place the source code in this folder, you should end up with the following files: + +src/Segger_SystemView/SEGGER_SYSVIEW_Int.h +src/Segger_SystemView/Global.h +src/Segger_SystemView/SEGGER.h +src/Segger_SystemView/SEGGER_RTT.c +src/Segger_SystemView/SEGGER_RTT.h +src/Segger_SystemView/SEGGER_RTT_Conf.h +src/Segger_SystemView/SEGGER_SYSVIEW.c +src/Segger_SystemView/SEGGER_SYSVIEW.h +src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h +src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h +src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c diff --git a/src/Segger_SystemView/SEGGER.h b/src/Segger_SystemView/SEGGER.h deleted file mode 100755 index 7c49a5c7..00000000 --- a/src/Segger_SystemView/SEGGER.h +++ /dev/null @@ -1,155 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** ----------------------------------------------------------------------- -File : SEGGER.h -Purpose : Global types etc & general purpose utility functions ----------------------------END-OF-HEADER------------------------------ -*/ - -#ifndef SEGGER_H // Guard against multiple inclusion -#define SEGGER_H - -#include "Global.h" // Type definitions: U8, U16, U32, I8, I16, I32 - -#if defined(__cplusplus) -extern "C" { /* Make sure we have C-declarations in C++ programs */ -#endif - -/********************************************************************* -* -* Keywords/specifiers -* -********************************************************************** -*/ - -#ifndef INLINE - #ifdef _WIN32 - // - // Microsoft VC6 and newer. - // Force inlining without cost checking. - // - #define INLINE __forceinline - #else - #if (defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) || defined(__RX) || defined(__ICCRX__)) - // - // Other known compilers. - // - #define INLINE inline - #else - // - // Unknown compilers. - // - #define INLINE - #endif - #endif -#endif - -/********************************************************************* -* -* Function-like macros -* -********************************************************************** -*/ - -#define SEGGER_COUNTOF(a) (sizeof((a))/sizeof((a)[0])) -#define SEGGER_MIN(a,b) (((a) < (b)) ? (a) : (b)) -#define SEGGER_MAX(a,b) (((a) > (b)) ? (a) : (b)) - -/********************************************************************* -* -* Types -* -********************************************************************** -*/ - -typedef struct { - char *pBuffer; - int BufferSize; - int Cnt; -} SEGGER_BUFFER_DESC; - -typedef struct { - int CacheLineSize; // 0: No Cache. Most Systems such as ARM9 use a 32 bytes cache line size. - void (*pfDMB) (void); // Optional DMB function for Data Memory Barrier to make sure all memory operations are completed. - void (*pfClean) (void *p, unsigned NumBytes); // Optional clean function for cached memory. - void (*pfInvalidate)(void *p, unsigned NumBytes); // Optional invalidate function for cached memory. -} SEGGER_CACHE_CONFIG; - -/********************************************************************* -* -* Utility functions -* -********************************************************************** -*/ - -void SEGGER_ARM_memcpy (void *pDest, const void *pSrc, int NumBytes); -void SEGGER_memcpy (void *pDest, const void *pSrc, int NumBytes); -void SEGGER_memxor (void *pDest, const void *pSrc, unsigned NumBytes); -void SEGGER_StoreChar (SEGGER_BUFFER_DESC *p, char c); -void SEGGER_PrintUnsigned(SEGGER_BUFFER_DESC *pBufferDesc, U32 v, unsigned Base, int NumDigits); -void SEGGER_PrintInt (SEGGER_BUFFER_DESC *pBufferDesc, I32 v, unsigned Base, unsigned NumDigits); -int SEGGER_snprintf (char *pBuffer, int BufferSize, const char *sFormat, ...); - - -#if defined(__cplusplus) -} /* Make sure we have C-declarations in C++ programs */ -#endif - -#endif // Avoid multiple inclusion - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_RTT.c b/src/Segger_SystemView/SEGGER_RTT.c deleted file mode 100755 index 4496d25c..00000000 --- a/src/Segger_SystemView/SEGGER_RTT.c +++ /dev/null @@ -1,1385 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** ----------------------------END-OF-HEADER------------------------------ -File : SEGGER_RTT.c -Purpose : Implementation of SEGGER real-time transfer (RTT) which - allows real-time communication on targets which support - debugger memory accesses while the CPU is running. -Revision: $Rev: 3882 $ - -Additional information: - Type "int" is assumed to be 32-bits in size - H->T Host to target communication - T->H Target to host communication - - RTT channel 0 is always present and reserved for Terminal usage. - Name is fixed to "Terminal" - - Effective buffer size: SizeOfBuffer - 1 - - WrOff == RdOff: Buffer is empty - WrOff == (RdOff - 1): Buffer is full - WrOff > RdOff: Free space includes wrap-around - WrOff < RdOff: Used space includes wrap-around - (WrOff == (SizeOfBuffer - 1)) && (RdOff == 0): - Buffer full and wrap-around after next byte - - ----------------------------------------------------------------------- -*/ - -#include "SEGGER_RTT.h" - -#include // for memcpy - -/********************************************************************* -* -* Configuration, default values -* -********************************************************************** -*/ - -#ifndef BUFFER_SIZE_UP - #define BUFFER_SIZE_UP 1024 // Size of the buffer for terminal output of target, up to host -#endif - -#ifndef BUFFER_SIZE_DOWN - #define BUFFER_SIZE_DOWN 16 // Size of the buffer for terminal input to target from host (Usually keyboard input) -#endif - -#ifndef SEGGER_RTT_MAX_NUM_UP_BUFFERS - #define SEGGER_RTT_MAX_NUM_UP_BUFFERS 2 // Number of up-buffers (T->H) available on this target -#endif - -#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS - #define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS 2 // Number of down-buffers (H->T) available on this target -#endif - -#ifndef SEGGER_RTT_BUFFER_SECTION - #if defined(SEGGER_RTT_SECTION) - #define SEGGER_RTT_BUFFER_SECTION SEGGER_RTT_SECTION - #endif -#endif - -#ifndef SEGGER_RTT_ALIGNMENT - #define SEGGER_RTT_ALIGNMENT 0 -#endif - -#ifndef SEGGER_RTT_BUFFER_ALIGNMENT - #define SEGGER_RTT_BUFFER_ALIGNMENT 0 -#endif - -#ifndef SEGGER_RTT_MODE_DEFAULT - #define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP -#endif - -#ifndef SEGGER_RTT_LOCK - #define SEGGER_RTT_LOCK() -#endif - -#ifndef SEGGER_RTT_UNLOCK - #define SEGGER_RTT_UNLOCK() -#endif - -#ifndef STRLEN - #define STRLEN(a) strlen((a)) -#endif - -#ifndef MEMCPY - #define MEMCPY(pDest, pSrc, NumBytes) memcpy((pDest), (pSrc), (NumBytes)) -#endif - -#ifndef MIN - #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX - #define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif -// -// For some environments, NULL may not be defined until certain headers are included -// -#ifndef NULL - #define NULL 0 -#endif - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ -#if (defined __ICCARM__) || (defined __ICCRX__) - #define RTT_PRAGMA(P) _Pragma(#P) -#endif - -#if SEGGER_RTT_ALIGNMENT || SEGGER_RTT_BUFFER_ALIGNMENT - #if (defined __GNUC__) - #define SEGGER_RTT_ALIGN(Var, Alignment) Var __attribute__ ((aligned (Alignment))) - #elif (defined __ICCARM__) || (defined __ICCRX__) - #define PRAGMA(A) _Pragma(#A) -#define SEGGER_RTT_ALIGN(Var, Alignment) RTT_PRAGMA(data_alignment=Alignment) \ - Var - #elif (defined __CC_ARM__) - #define SEGGER_RTT_ALIGN(Var, Alignment) Var __attribute__ ((aligned (Alignment))) - #else - #error "Alignment not supported for this compiler." - #endif -#else - #define SEGGER_RTT_ALIGN(Var, Alignment) Var -#endif - -#if defined(SEGGER_RTT_SECTION) || defined (SEGGER_RTT_BUFFER_SECTION) - #if (defined __GNUC__) - #define SEGGER_RTT_PUT_SECTION(Var, Section) __attribute__ ((section (Section))) Var - #elif (defined __ICCARM__) || (defined __ICCRX__) -#define SEGGER_RTT_PUT_SECTION(Var, Section) RTT_PRAGMA(location=Section) \ - Var - #elif (defined __CC_ARM__) - #define SEGGER_RTT_PUT_SECTION(Var, Section) __attribute__ ((section (Section), zero_init)) Var - #else - #error "Section placement not supported for this compiler." - #endif -#else - #define SEGGER_RTT_PUT_SECTION(Var, Section) Var -#endif - - -#if SEGGER_RTT_ALIGNMENT - #define SEGGER_RTT_CB_ALIGN(Var) SEGGER_RTT_ALIGN(Var, SEGGER_RTT_ALIGNMENT) -#else - #define SEGGER_RTT_CB_ALIGN(Var) Var -#endif - -#if SEGGER_RTT_BUFFER_ALIGNMENT - #define SEGGER_RTT_BUFFER_ALIGN(Var) SEGGER_RTT_ALIGN(Var, SEGGER_RTT_BUFFER_ALIGNMENT) -#else - #define SEGGER_RTT_BUFFER_ALIGN(Var) Var -#endif - - -#if defined(SEGGER_RTT_SECTION) - #define SEGGER_RTT_PUT_CB_SECTION(Var) SEGGER_RTT_PUT_SECTION(Var, SEGGER_RTT_SECTION) -#else - #define SEGGER_RTT_PUT_CB_SECTION(Var) Var -#endif - -#if defined(SEGGER_RTT_BUFFER_SECTION) - #define SEGGER_RTT_PUT_BUFFER_SECTION(Var) SEGGER_RTT_PUT_SECTION(Var, SEGGER_RTT_BUFFER_SECTION) -#else - #define SEGGER_RTT_PUT_BUFFER_SECTION(Var) Var -#endif - -/********************************************************************* -* -* Static const data -* -********************************************************************** -*/ - -static unsigned char _aTerminalId[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - -/********************************************************************* -* -* Static data -* -********************************************************************** -*/ -// -// RTT Control Block and allocate buffers for channel 0 -// -SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT)); - -SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acUpBuffer [BUFFER_SIZE_UP])); -SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acDownBuffer[BUFFER_SIZE_DOWN])); - -static char _ActiveTerminal; - -/********************************************************************* -* -* Static functions -* -********************************************************************** -*/ - -/********************************************************************* -* -* _DoInit() -* -* Function description -* Initializes the control block an buffers. -* May only be called via INIT() to avoid overriding settings. -* -*/ -#define INIT() do { \ - if (_SEGGER_RTT.acID[0] == '\0') { _DoInit(); } \ - } while (0) -static void _DoInit(void) { - SEGGER_RTT_CB* p; - // - // Initialize control block - // - p = &_SEGGER_RTT; - p->MaxNumUpBuffers = SEGGER_RTT_MAX_NUM_UP_BUFFERS; - p->MaxNumDownBuffers = SEGGER_RTT_MAX_NUM_DOWN_BUFFERS; - // - // Initialize up buffer 0 - // - p->aUp[0].sName = "Terminal"; - p->aUp[0].pBuffer = _acUpBuffer; - p->aUp[0].SizeOfBuffer = sizeof(_acUpBuffer); - p->aUp[0].RdOff = 0u; - p->aUp[0].WrOff = 0u; - p->aUp[0].Flags = SEGGER_RTT_MODE_DEFAULT; - // - // Initialize down buffer 0 - // - p->aDown[0].sName = "Terminal"; - p->aDown[0].pBuffer = _acDownBuffer; - p->aDown[0].SizeOfBuffer = sizeof(_acDownBuffer); - p->aDown[0].RdOff = 0u; - p->aDown[0].WrOff = 0u; - p->aDown[0].Flags = SEGGER_RTT_MODE_DEFAULT; - // - // Finish initialization of the control block. - // Copy Id string in three steps to make sure "SEGGER RTT" is not found - // in initializer memory (usually flash) by J-Link - // - strcpy(&p->acID[7], "RTT"); - strcpy(&p->acID[0], "SEGGER"); - p->acID[6] = ' '; -} - -/********************************************************************* -* -* _WriteBlocking() -* -* Function description -* Stores a specified number of characters in SEGGER RTT ring buffer -* and updates the associated write pointer which is periodically -* read by the host. -* The caller is responsible for managing the write chunk sizes as -* _WriteBlocking() will block until all data has been posted successfully. -* -* Parameters -* pRing Ring buffer to post to. -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* >= 0 - Number of bytes written into buffer. -*/ -static unsigned _WriteBlocking(SEGGER_RTT_BUFFER_UP* pRing, const char* pBuffer, unsigned NumBytes) { - unsigned NumBytesToWrite; - unsigned NumBytesWritten; - unsigned RdOff; - unsigned WrOff; - // - // Write data to buffer and handle wrap-around if necessary - // - NumBytesWritten = 0u; - WrOff = pRing->WrOff; - do { - RdOff = pRing->RdOff; // May be changed by host (debug probe) in the meantime - if (RdOff > WrOff) { - NumBytesToWrite = RdOff - WrOff - 1u; - } else { - NumBytesToWrite = pRing->SizeOfBuffer - (WrOff - RdOff + 1u); - } - NumBytesToWrite = MIN(NumBytesToWrite, (pRing->SizeOfBuffer - WrOff)); // Number of bytes that can be written until buffer wrap-around - NumBytesToWrite = MIN(NumBytesToWrite, NumBytes); - memcpy(pRing->pBuffer + WrOff, pBuffer, NumBytesToWrite); - NumBytesWritten += NumBytesToWrite; - pBuffer += NumBytesToWrite; - NumBytes -= NumBytesToWrite; - WrOff += NumBytesToWrite; - if (WrOff == pRing->SizeOfBuffer) { - WrOff = 0u; - } - pRing->WrOff = WrOff; - } while (NumBytes); - // - return NumBytesWritten; -} - -/********************************************************************* -* -* _WriteNoCheck() -* -* Function description -* Stores a specified number of characters in SEGGER RTT ring buffer -* and updates the associated write pointer which is periodically -* read by the host. -* It is callers responsibility to make sure data actually fits in buffer. -* -* Parameters -* pRing Ring buffer to post to. -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Notes -* (1) If there might not be enough space in the "Up"-buffer, call _WriteBlocking -*/ -static void _WriteNoCheck(SEGGER_RTT_BUFFER_UP* pRing, const char* pData, unsigned NumBytes) { - unsigned NumBytesAtOnce; - unsigned WrOff; - unsigned Rem; - - WrOff = pRing->WrOff; - Rem = pRing->SizeOfBuffer - WrOff; - if (Rem > NumBytes) { - // - // All data fits before wrap around - // - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; - } else { - // - // We reach the end of the buffer, so need to wrap around - // - NumBytesAtOnce = Rem; - memcpy(pRing->pBuffer + WrOff, pData, NumBytesAtOnce); - NumBytesAtOnce = NumBytes - Rem; - memcpy(pRing->pBuffer, pData + Rem, NumBytesAtOnce); - pRing->WrOff = NumBytesAtOnce; - } -} - -/********************************************************************* -* -* _PostTerminalSwitch() -* -* Function description -* Switch terminal to the given terminal ID. It is the caller's -* responsibility to ensure the terminal ID is correct and there is -* enough space in the buffer for this to complete successfully. -* -* Parameters -* pRing Ring buffer to post to. -* TerminalId Terminal ID to switch to. -*/ -static void _PostTerminalSwitch(SEGGER_RTT_BUFFER_UP* pRing, unsigned char TerminalId) { - char ac[2]; - - ac[0] = 0xFFu; - ac[1] = _aTerminalId[TerminalId]; // Caller made already sure that TerminalId does not exceed our terminal limit - _WriteBlocking(pRing, ac, 2u); -} - -/********************************************************************* -* -* _GetAvailWriteSpace() -* -* Function description -* Returns the number of bytes that can be written to the ring -* buffer without blocking. -* -* Parameters -* pRing Ring buffer to check. -* -* Return value -* Number of bytes that are free in the buffer. -*/ -static unsigned _GetAvailWriteSpace(SEGGER_RTT_BUFFER_UP* pRing) { - unsigned RdOff; - unsigned WrOff; - unsigned r; - // - // Avoid warnings regarding volatile access order. It's not a problem - // in this case, but dampen compiler enthusiasm. - // - RdOff = pRing->RdOff; - WrOff = pRing->WrOff; - if (RdOff <= WrOff) { - r = pRing->SizeOfBuffer - 1u - WrOff + RdOff; - } else { - r = RdOff - WrOff - 1u; - } - return r; -} - -/********************************************************************* -* -* Public code -* -********************************************************************** -*/ -/********************************************************************* -* -* SEGGER_RTT_ReadNoLock() -* -* Function description -* Reads characters from SEGGER real-time-terminal control block -* which have been previously stored by the host. -* Do not lock against interrupts and multiple access. -* -* Parameters -* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. -* BufferSize Size of the target application buffer. -* -* Return value -* Number of bytes that have been read. -*/ -unsigned SEGGER_RTT_ReadNoLock(unsigned BufferIndex, void* pData, unsigned BufferSize) { - unsigned NumBytesRem; - unsigned NumBytesRead; - unsigned RdOff; - unsigned WrOff; - unsigned char* pBuffer; - SEGGER_RTT_BUFFER_DOWN* pRing; - // - INIT(); - pRing = &_SEGGER_RTT.aDown[BufferIndex]; - pBuffer = (unsigned char*)pData; - RdOff = pRing->RdOff; - WrOff = pRing->WrOff; - NumBytesRead = 0u; - // - // Read from current read position to wrap-around of buffer, first - // - if (RdOff > WrOff) { - NumBytesRem = pRing->SizeOfBuffer - RdOff; - NumBytesRem = MIN(NumBytesRem, BufferSize); - memcpy(pBuffer, pRing->pBuffer + RdOff, NumBytesRem); - NumBytesRead += NumBytesRem; - pBuffer += NumBytesRem; - BufferSize -= NumBytesRem; - RdOff += NumBytesRem; - // - // Handle wrap-around of buffer - // - if (RdOff == pRing->SizeOfBuffer) { - RdOff = 0u; - } - } - // - // Read remaining items of buffer - // - NumBytesRem = WrOff - RdOff; - NumBytesRem = MIN(NumBytesRem, BufferSize); - if (NumBytesRem > 0u) { - memcpy(pBuffer, pRing->pBuffer + RdOff, NumBytesRem); - NumBytesRead += NumBytesRem; - pBuffer += NumBytesRem; - BufferSize -= NumBytesRem; - RdOff += NumBytesRem; - } - if (NumBytesRead) { - pRing->RdOff = RdOff; - } - // - return NumBytesRead; -} - -/********************************************************************* -* -* SEGGER_RTT_Read -* -* Function description -* Reads characters from SEGGER real-time-terminal control block -* which have been previously stored by the host. -* -* Parameters -* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. -* BufferSize Size of the target application buffer. -* -* Return value -* Number of bytes that have been read. -*/ -unsigned SEGGER_RTT_Read(unsigned BufferIndex, void* pBuffer, unsigned BufferSize) { - unsigned NumBytesRead; - // - SEGGER_RTT_LOCK(); - // - // Call the non-locking read function - // - NumBytesRead = SEGGER_RTT_ReadNoLock(BufferIndex, pBuffer, BufferSize); - // - // Finish up. - // - SEGGER_RTT_UNLOCK(); - // - return NumBytesRead; -} - -/********************************************************************* -* -* SEGGER_RTT_WriteWithOverwriteNoLock -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block. -* SEGGER_RTT_WriteWithOverwriteNoLock does not lock the application -* and overwrites data if the data does not fit into the buffer. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, data is overwritten. -* (2) For performance reasons this function does not call Init() -* and may only be called after RTT has been initialized. -* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. -* (3) Do not use SEGGER_RTT_WriteWithOverwriteNoLock if a J-Link -* connection reads RTT data. -*/ -void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - const char* pData; - SEGGER_RTT_BUFFER_UP* pRing; - unsigned Avail; - - pData = (const char *)pBuffer; - // - // Get "to-host" ring buffer and copy some elements into local variables. - // - pRing = &_SEGGER_RTT.aUp[BufferIndex]; - // - // Check if we will overwrite data and need to adjust the RdOff. - // - if (pRing->WrOff == pRing->RdOff) { - Avail = pRing->SizeOfBuffer - 1u; - } else if ( pRing->WrOff < pRing->RdOff) { - Avail = pRing->RdOff - pRing->WrOff - 1u; - } else { - Avail = pRing->RdOff - pRing->WrOff - 1u + pRing->SizeOfBuffer; - } - if (NumBytes > Avail) { - pRing->RdOff += (NumBytes - Avail); - while (pRing->RdOff >= pRing->SizeOfBuffer) { - pRing->RdOff -= pRing->SizeOfBuffer; - } - } - // - // Write all data, no need to check the RdOff, but possibly handle multiple wrap-arounds - // - Avail = pRing->SizeOfBuffer - pRing->WrOff; - do { - if (Avail > NumBytes) { - // - // Last round - // -#if 1 // memcpy() is good for large amounts of data, but the overhead is too big for small amounts. Use a simple byte loop instead. - char* pDst; - pDst = pRing->pBuffer + pRing->WrOff; - pRing->WrOff += NumBytes; - do { - *pDst++ = *pData++; - } while (--NumBytes); -#else - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff += NumBytes; -#endif - break; //Alternatively: NumBytes = 0; - } else { - // - // Wrap-around necessary, write until wrap-around and reset WrOff - // - memcpy(pRing->pBuffer + pRing->WrOff, pData, Avail); - pData += Avail; - pRing->WrOff = 0; - NumBytes -= Avail; - Avail = (pRing->SizeOfBuffer - 1); - } - } while (NumBytes); -} - -/********************************************************************* -* -* SEGGER_RTT_WriteSkipNoLock -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block which is then read by the host. -* SEGGER_RTT_WriteSkipNoLock does not lock the application and -* skips all data, if the data does not fit into the buffer. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, all data is dropped. -* (2) For performance reasons this function does not call Init() -* and may only be called after RTT has been initialized. -* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. -*/ -unsigned SEGGER_RTT_WriteSkipNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - const char* pData; - SEGGER_RTT_BUFFER_UP* pRing; - unsigned Avail; - unsigned RdOff; - unsigned WrOff; - unsigned Rem; - - pData = (const char *)pBuffer; - // - // Get "to-host" ring buffer and copy some elements into local variables. - // - pRing = &_SEGGER_RTT.aUp[BufferIndex]; - RdOff = pRing->RdOff; - WrOff = pRing->WrOff; - // - // Handle the most common cases fastest. - // Which is: - // RdOff <= WrOff -> Space until wrap around is free. - // AND - // WrOff + NumBytes < SizeOfBuffer -> No Wrap around necessary. - // - // OR - // - // RdOff > WrOff -> Space until RdOff - 1 is free. - // AND - // WrOff + NumBytes < RdOff -> Data fits into buffer - // - if (RdOff <= WrOff) { - // - // Get space until WrOff will be at wrap around. - // - Avail = pRing->SizeOfBuffer - 1u - WrOff ; - if (Avail >= NumBytes) { -#if 1 // memcpy() is good for large amounts of data, but the overhead is too big for small amounts. Use a simple byte loop instead. - char* pDst; - pDst = pRing->pBuffer + WrOff; - WrOff += NumBytes; - do { - *pDst++ = *pData++; - } while (--NumBytes); - pRing->WrOff = WrOff + NumBytes; -#else - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; -#endif - return 1; - } - // - // If data did not fit into space until wrap around calculate complete space in buffer. - // - Avail += RdOff; - // - // If there is still no space for the whole of this output, don't bother. - // - if (Avail >= NumBytes) { - // - // OK, we have enough space in buffer. Copy in one or 2 chunks - // - Rem = pRing->SizeOfBuffer - WrOff; // Space until end of buffer - if (Rem > NumBytes) { - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; - } else { - // - // We reach the end of the buffer, so need to wrap around - // - memcpy(pRing->pBuffer + WrOff, pData, Rem); - memcpy(pRing->pBuffer, pData + Rem, NumBytes - Rem); - pRing->WrOff = NumBytes - Rem; - } - return 1; - } - } else { - Avail = RdOff - WrOff - 1u; - if (Avail >= NumBytes) { - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; - return 1; - } - } - // - // If we reach this point no data has been written - // - return 0; -} - -/********************************************************************* -* -* SEGGER_RTT_WriteNoLock -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block which is then read by the host. -* SEGGER_RTT_WriteNoLock does not lock the application. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, remaining characters of pBuffer are dropped. -* (2) For performance reasons this function does not call Init() -* and may only be called after RTT has been initialized. -* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. -*/ -unsigned SEGGER_RTT_WriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - unsigned Status; - unsigned Avail; - const char* pData; - SEGGER_RTT_BUFFER_UP* pRing; - - pData = (const char *)pBuffer; - // - // Get "to-host" ring buffer. - // - pRing = &_SEGGER_RTT.aUp[BufferIndex]; - // - // How we output depends upon the mode... - // - switch (pRing->Flags) { - case SEGGER_RTT_MODE_NO_BLOCK_SKIP: - // - // If we are in skip mode and there is no space for the whole - // of this output, don't bother. - // - Avail = _GetAvailWriteSpace(pRing); - if (Avail < NumBytes) { - Status = 0u; - } else { - Status = NumBytes; - _WriteNoCheck(pRing, pData, NumBytes); - } - break; - case SEGGER_RTT_MODE_NO_BLOCK_TRIM: - // - // If we are in trim mode, trim to what we can output without blocking. - // - Avail = _GetAvailWriteSpace(pRing); - Status = Avail < NumBytes ? Avail : NumBytes; - _WriteNoCheck(pRing, pData, Status); - break; - case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: - // - // If we are in blocking mode, output everything. - // - Status = _WriteBlocking(pRing, pData, NumBytes); - break; - default: - Status = 0u; - break; - } - // - // Finish up. - // - return Status; -} - -/********************************************************************* -* -* SEGGER_RTT_Write -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block which is then read by the host. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, remaining characters of pBuffer are dropped. -*/ -unsigned SEGGER_RTT_Write(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - unsigned Status; - // - INIT(); - SEGGER_RTT_LOCK(); - // - // Call the non-locking write function - // - Status = SEGGER_RTT_WriteNoLock(BufferIndex, pBuffer, NumBytes); - // - // Finish up. - // - SEGGER_RTT_UNLOCK(); - // - return Status; -} - -/********************************************************************* -* -* SEGGER_RTT_WriteString -* -* Function description -* Stores string in SEGGER RTT control block. -* This data is read by the host. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* s Pointer to string. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, depending on configuration, -* remaining characters may be dropped or RTT module waits until there is more space in the buffer. -* (2) String passed to this function has to be \0 terminated -* (3) \0 termination character is *not* stored in RTT buffer -*/ -unsigned SEGGER_RTT_WriteString(unsigned BufferIndex, const char* s) { - unsigned Len; - - Len = STRLEN(s); - return SEGGER_RTT_Write(BufferIndex, s, Len); -} - -/********************************************************************* -* -* SEGGER_RTT_GetKey -* -* Function description -* Reads one character from the SEGGER RTT buffer. -* Host has previously stored data there. -* -* Return value -* < 0 - No character available (buffer empty). -* >= 0 - Character which has been read. (Possible values: 0 - 255) -* -* Notes -* (1) This function is only specified for accesses to RTT buffer 0. -*/ -int SEGGER_RTT_GetKey(void) { - char c; - int r; - - r = (int)SEGGER_RTT_Read(0u, &c, 1u); - if (r == 1) { - r = (int)(unsigned char)c; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_WaitKey -* -* Function description -* Waits until at least one character is avaible in the SEGGER RTT buffer. -* Once a character is available, it is read and this function returns. -* -* Return value -* >=0 - Character which has been read. -* -* Notes -* (1) This function is only specified for accesses to RTT buffer 0 -* (2) This function is blocking if no character is present in RTT buffer -*/ -int SEGGER_RTT_WaitKey(void) { - int r; - - do { - r = SEGGER_RTT_GetKey(); - } while (r < 0); - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_HasKey -* -* Function description -* Checks if at least one character for reading is available in the SEGGER RTT buffer. -* -* Return value -* == 0 - No characters are available to read. -* == 1 - At least one character is available. -* -* Notes -* (1) This function is only specified for accesses to RTT buffer 0 -*/ -int SEGGER_RTT_HasKey(void) { - unsigned RdOff; - int r; - - INIT(); - RdOff = _SEGGER_RTT.aDown[0].RdOff; - if (RdOff != _SEGGER_RTT.aDown[0].WrOff) { - r = 1; - } else { - r = 0; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_HasData -* -* Function description -* Check if there is data from the host in the given buffer. -* -* Return value: -* ==0: No data -* !=0: Data in buffer -* -*/ -unsigned SEGGER_RTT_HasData(unsigned BufferIndex) { - SEGGER_RTT_BUFFER_DOWN* pRing; - unsigned v; - - pRing = &_SEGGER_RTT.aDown[BufferIndex]; - v = pRing->WrOff; - return v - pRing->RdOff; -} - -/********************************************************************* -* -* SEGGER_RTT_AllocDownBuffer -* -* Function description -* Run-time configuration of the next down-buffer (H->T). -* The next buffer, which is not used yet is configured. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 - O.K. Buffer Index -* < 0 - Error -*/ -int SEGGER_RTT_AllocDownBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int BufferIndex; - - INIT(); - SEGGER_RTT_LOCK(); - BufferIndex = 0; - do { - if (_SEGGER_RTT.aDown[BufferIndex].pBuffer == NULL) { - break; - } - BufferIndex++; - } while (BufferIndex < _SEGGER_RTT.MaxNumDownBuffers); - if (BufferIndex < _SEGGER_RTT.MaxNumDownBuffers) { - _SEGGER_RTT.aDown[BufferIndex].sName = sName; - _SEGGER_RTT.aDown[BufferIndex].pBuffer = (char*)pBuffer; - _SEGGER_RTT.aDown[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aDown[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aDown[BufferIndex].WrOff = 0u; - _SEGGER_RTT.aDown[BufferIndex].Flags = Flags; - } else { - BufferIndex = -1; - } - SEGGER_RTT_UNLOCK(); - return BufferIndex; -} - -/********************************************************************* -* -* SEGGER_RTT_AllocUpBuffer -* -* Function description -* Run-time configuration of the next up-buffer (T->H). -* The next buffer, which is not used yet is configured. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 - O.K. Buffer Index -* < 0 - Error -*/ -int SEGGER_RTT_AllocUpBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int BufferIndex; - - INIT(); - SEGGER_RTT_LOCK(); - BufferIndex = 0; - do { - if (_SEGGER_RTT.aUp[BufferIndex].pBuffer == NULL) { - break; - } - BufferIndex++; - } while (BufferIndex < _SEGGER_RTT.MaxNumUpBuffers); - if (BufferIndex < _SEGGER_RTT.MaxNumUpBuffers) { - _SEGGER_RTT.aUp[BufferIndex].sName = sName; - _SEGGER_RTT.aUp[BufferIndex].pBuffer = (char*)pBuffer; - _SEGGER_RTT.aUp[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aUp[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aUp[BufferIndex].WrOff = 0u; - _SEGGER_RTT.aUp[BufferIndex].Flags = Flags; - } else { - BufferIndex = -1; - } - SEGGER_RTT_UNLOCK(); - return BufferIndex; -} - -/********************************************************************* -* -* SEGGER_RTT_ConfigUpBuffer -* -* Function description -* Run-time configuration of a specific up-buffer (T->H). -* Buffer to be configured is specified by index. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* BufferIndex Index of the buffer to configure. -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 - O.K. -* < 0 - Error -*/ -int SEGGER_RTT_ConfigUpBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumUpBuffers) { - SEGGER_RTT_LOCK(); - if (BufferIndex > 0u) { - _SEGGER_RTT.aUp[BufferIndex].sName = sName; - _SEGGER_RTT.aUp[BufferIndex].pBuffer = (char*)pBuffer; - _SEGGER_RTT.aUp[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aUp[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aUp[BufferIndex].WrOff = 0u; - } - _SEGGER_RTT.aUp[BufferIndex].Flags = Flags; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_ConfigDownBuffer -* -* Function description -* Run-time configuration of a specific down-buffer (H->T). -* Buffer to be configured is specified by index. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* BufferIndex Index of the buffer to configure. -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 O.K. -* < 0 Error -*/ -int SEGGER_RTT_ConfigDownBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumDownBuffers) { - SEGGER_RTT_LOCK(); - if (BufferIndex > 0u) { - _SEGGER_RTT.aDown[BufferIndex].sName = sName; - _SEGGER_RTT.aDown[BufferIndex].pBuffer = (char*)pBuffer; - _SEGGER_RTT.aDown[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aDown[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aDown[BufferIndex].WrOff = 0u; - } - _SEGGER_RTT.aDown[BufferIndex].Flags = Flags; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_SetNameUpBuffer -* -* Function description -* Run-time configuration of a specific up-buffer name (T->H). -* Buffer to be configured is specified by index. -* -* Parameters -* BufferIndex Index of the buffer to renamed. -* sName Pointer to a constant name string. -* -* Return value -* >= 0 O.K. -* < 0 Error -*/ -int SEGGER_RTT_SetNameUpBuffer(unsigned BufferIndex, const char* sName) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumUpBuffers) { - SEGGER_RTT_LOCK(); - _SEGGER_RTT.aUp[BufferIndex].sName = sName; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_SetNameDownBuffer -* -* Function description -* Run-time configuration of a specific Down-buffer name (T->H). -* Buffer to be configured is specified by index. -* -* Parameters -* BufferIndex Index of the buffer to renamed. -* sName Pointer to a constant name string. -* -* Return value -* >= 0 O.K. -* < 0 Error -*/ -int SEGGER_RTT_SetNameDownBuffer(unsigned BufferIndex, const char* sName) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumDownBuffers) { - SEGGER_RTT_LOCK(); - _SEGGER_RTT.aDown[BufferIndex].sName = sName; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_Init -* -* Function description -* Initializes the RTT Control Block. -* Should be used in RAM targets, at start of the application. -* -*/ -void SEGGER_RTT_Init (void) { - _DoInit(); -} - -/********************************************************************* -* -* SEGGER_RTT_SetTerminal -* -* Function description -* Sets the terminal to be used for output on channel 0. -* -* Parameters -* TerminalId Index of the terminal. -* -* Return value -* >= 0 O.K. -* < 0 Error (e.g. if RTT is configured for non-blocking mode and there was no space in the buffer to set the new terminal Id) -*/ -int SEGGER_RTT_SetTerminal (char TerminalId) { - char ac[2]; - SEGGER_RTT_BUFFER_UP* pRing; - unsigned Avail; - int r; - // - INIT(); - // - r = 0; - ac[0] = 0xFFU; - if ((unsigned char)TerminalId < (unsigned char)sizeof(_aTerminalId)) { // We only support a certain number of channels - ac[1] = _aTerminalId[(unsigned char)TerminalId]; - pRing = &_SEGGER_RTT.aUp[0]; // Buffer 0 is always reserved for terminal I/O, so we can use index 0 here, fixed - SEGGER_RTT_LOCK(); // Lock to make sure that no other task is writing into buffer, while we are and number of free bytes in buffer does not change downwards after checking and before writing - if ((pRing->Flags & SEGGER_RTT_MODE_MASK) == SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL) { - _ActiveTerminal = TerminalId; - _WriteBlocking(pRing, ac, 2u); - } else { // Skipping mode or trim mode? => We cannot trim this command so handling is the same for both modes - Avail = _GetAvailWriteSpace(pRing); - if (Avail >= 2) { - _ActiveTerminal = TerminalId; // Only change active terminal in case of success - _WriteNoCheck(pRing, ac, 2u); - } else { - r = -1; - } - } - SEGGER_RTT_UNLOCK(); - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_TerminalOut -* -* Function description -* Writes a string to the given terminal -* without changing the terminal for channel 0. -* -* Parameters -* TerminalId Index of the terminal. -* s String to be printed on the terminal. -* -* Return value -* >= 0 - Number of bytes written. -* < 0 - Error. -* -*/ -int SEGGER_RTT_TerminalOut (char TerminalId, const char* s) { - int Status; - unsigned FragLen; - unsigned Avail; - SEGGER_RTT_BUFFER_UP* pRing; - // - INIT(); - // - // Validate terminal ID. - // - if (TerminalId < (char)sizeof(_aTerminalId)) { // We only support a certain number of channels - // - // Get "to-host" ring buffer. - // - pRing = &_SEGGER_RTT.aUp[0]; - // - // Need to be able to change terminal, write data, change back. - // Compute the fixed and variable sizes. - // - FragLen = strlen(s); - // - // How we output depends upon the mode... - // - SEGGER_RTT_LOCK(); - Avail = _GetAvailWriteSpace(pRing); - switch (pRing->Flags & SEGGER_RTT_MODE_MASK) { - case SEGGER_RTT_MODE_NO_BLOCK_SKIP: - // - // If we are in skip mode and there is no space for the whole - // of this output, don't bother switching terminals at all. - // - if (Avail < (FragLen + 4u)) { - Status = 0; - } else { - _PostTerminalSwitch(pRing, TerminalId); - Status = (int)_WriteBlocking(pRing, s, FragLen); - _PostTerminalSwitch(pRing, _ActiveTerminal); - } - break; - case SEGGER_RTT_MODE_NO_BLOCK_TRIM: - // - // If we are in trim mode and there is not enough space for everything, - // trim the output but always include the terminal switch. If no room - // for terminal switch, skip that totally. - // - if (Avail < 4u) { - Status = -1; - } else { - _PostTerminalSwitch(pRing, TerminalId); - Status = (int)_WriteBlocking(pRing, s, (FragLen < (Avail - 4u)) ? FragLen : (Avail - 4u)); - _PostTerminalSwitch(pRing, _ActiveTerminal); - } - break; - case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: - // - // If we are in blocking mode, output everything. - // - _PostTerminalSwitch(pRing, TerminalId); - Status = (int)_WriteBlocking(pRing, s, FragLen); - _PostTerminalSwitch(pRing, _ActiveTerminal); - break; - default: - Status = -1; - break; - } - // - // Finish up. - // - SEGGER_RTT_UNLOCK(); - } else { - Status = -1; - } - return Status; -} - - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_RTT.h b/src/Segger_SystemView/SEGGER_RTT.h deleted file mode 100755 index 25a4fea9..00000000 --- a/src/Segger_SystemView/SEGGER_RTT.h +++ /dev/null @@ -1,246 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** ----------------------------END-OF-HEADER------------------------------ -File : SEGGER_RTT.h -Purpose : Implementation of SEGGER real-time transfer which allows - real-time communication on targets which support debugger - memory accesses while the CPU is running. -Revision: $Rev: 3667 $ ----------------------------------------------------------------------- -*/ - -#ifndef SEGGER_RTT_H -#define SEGGER_RTT_H - -#include "SEGGER_RTT_Conf.h" - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ - -/********************************************************************* -* -* Types -* -********************************************************************** -*/ - -// -// Description for a circular buffer (also called "ring buffer") -// which is used as up-buffer (T->H) -// -typedef struct { - const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" - char* pBuffer; // Pointer to start of buffer - unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. - unsigned WrOff; // Position of next item to be written by either target. - volatile unsigned RdOff; // Position of next item to be read by host. Must be volatile since it may be modified by host. - unsigned Flags; // Contains configuration flags -} SEGGER_RTT_BUFFER_UP; - -// -// Description for a circular buffer (also called "ring buffer") -// which is used as down-buffer (H->T) -// -typedef struct { - const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" - char* pBuffer; // Pointer to start of buffer - unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. - volatile unsigned WrOff; // Position of next item to be written by host. Must be volatile since it may be modified by host. - unsigned RdOff; // Position of next item to be read by target (down-buffer). - unsigned Flags; // Contains configuration flags -} SEGGER_RTT_BUFFER_DOWN; - -// -// RTT control block which describes the number of buffers available -// as well as the configuration for each buffer -// -// -typedef struct { - char acID[16]; // Initialized to "SEGGER RTT" - int MaxNumUpBuffers; // Initialized to SEGGER_RTT_MAX_NUM_UP_BUFFERS (type. 2) - int MaxNumDownBuffers; // Initialized to SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (type. 2) - SEGGER_RTT_BUFFER_UP aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS]; // Up buffers, transferring information up from target via debug probe to host - SEGGER_RTT_BUFFER_DOWN aDown[SEGGER_RTT_MAX_NUM_DOWN_BUFFERS]; // Down buffers, transferring information down from host via debug probe to target -} SEGGER_RTT_CB; - -/********************************************************************* -* -* Global data -* -********************************************************************** -*/ -extern SEGGER_RTT_CB _SEGGER_RTT; - -/********************************************************************* -* -* RTT API functions -* -********************************************************************** -*/ -#ifdef __cplusplus - extern "C" { -#endif -int SEGGER_RTT_AllocDownBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_AllocUpBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_ConfigUpBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_ConfigDownBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_GetKey (void); -unsigned SEGGER_RTT_HasData (unsigned BufferIndex); -int SEGGER_RTT_HasKey (void); -void SEGGER_RTT_Init (void); -unsigned SEGGER_RTT_Read (unsigned BufferIndex, void* pBuffer, unsigned BufferSize); -unsigned SEGGER_RTT_ReadNoLock (unsigned BufferIndex, void* pData, unsigned BufferSize); -int SEGGER_RTT_SetNameDownBuffer(unsigned BufferIndex, const char* sName); -int SEGGER_RTT_SetNameUpBuffer (unsigned BufferIndex, const char* sName); -int SEGGER_RTT_WaitKey (void); -unsigned SEGGER_RTT_Write (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -unsigned SEGGER_RTT_WriteNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -unsigned SEGGER_RTT_WriteSkipNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -unsigned SEGGER_RTT_WriteString (unsigned BufferIndex, const char* s); -void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -// -// Function macro for performance optimization -// -#define SEGGER_RTT_HASDATA(n) (_SEGGER_RTT.aDown[n].WrOff - _SEGGER_RTT.aDown[n].RdOff) - -/********************************************************************* -* -* RTT "Terminal" API functions -* -********************************************************************** -*/ -int SEGGER_RTT_SetTerminal (char TerminalId); -int SEGGER_RTT_TerminalOut (char TerminalId, const char* s); - -/********************************************************************* -* -* RTT printf functions (require SEGGER_RTT_printf.c) -* -********************************************************************** -*/ -int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...); -#ifdef __cplusplus - } -#endif - -/********************************************************************* -* -* Defines -* -********************************************************************** -*/ - -// -// Operating modes. Define behavior if buffer is full (not enough space for entire message) -// -#define SEGGER_RTT_MODE_NO_BLOCK_SKIP (0U) // Skip. Do not block, output nothing. (Default) -#define SEGGER_RTT_MODE_NO_BLOCK_TRIM (1U) // Trim: Do not block, output as much as fits. -#define SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL (2U) // Block: Wait until there is space in the buffer. -#define SEGGER_RTT_MODE_MASK (3U) - -// -// Control sequences, based on ANSI. -// Can be used to control color, and clear the screen -// -#define RTT_CTRL_RESET "" // Reset to default colors -#define RTT_CTRL_CLEAR "" // Clear screen, reposition cursor to top left - -#define RTT_CTRL_TEXT_BLACK "" -#define RTT_CTRL_TEXT_RED "" -#define RTT_CTRL_TEXT_GREEN "" -#define RTT_CTRL_TEXT_YELLOW "" -#define RTT_CTRL_TEXT_BLUE "" -#define RTT_CTRL_TEXT_MAGENTA "" -#define RTT_CTRL_TEXT_CYAN "" -#define RTT_CTRL_TEXT_WHITE "" - -#define RTT_CTRL_TEXT_BRIGHT_BLACK "" -#define RTT_CTRL_TEXT_BRIGHT_RED "" -#define RTT_CTRL_TEXT_BRIGHT_GREEN "" -#define RTT_CTRL_TEXT_BRIGHT_YELLOW "" -#define RTT_CTRL_TEXT_BRIGHT_BLUE "" -#define RTT_CTRL_TEXT_BRIGHT_MAGENTA "" -#define RTT_CTRL_TEXT_BRIGHT_CYAN "" -#define RTT_CTRL_TEXT_BRIGHT_WHITE "" - -#define RTT_CTRL_BG_BLACK "" -#define RTT_CTRL_BG_RED "" -#define RTT_CTRL_BG_GREEN "" -#define RTT_CTRL_BG_YELLOW "" -#define RTT_CTRL_BG_BLUE "" -#define RTT_CTRL_BG_MAGENTA "" -#define RTT_CTRL_BG_CYAN "" -#define RTT_CTRL_BG_WHITE "" - -#define RTT_CTRL_BG_BRIGHT_BLACK "" -#define RTT_CTRL_BG_BRIGHT_RED "" -#define RTT_CTRL_BG_BRIGHT_GREEN "" -#define RTT_CTRL_BG_BRIGHT_YELLOW "" -#define RTT_CTRL_BG_BRIGHT_BLUE "" -#define RTT_CTRL_BG_BRIGHT_MAGENTA "" -#define RTT_CTRL_BG_BRIGHT_CYAN "" -#define RTT_CTRL_BG_BRIGHT_WHITE "" - - -#endif - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_RTT_Conf.h b/src/Segger_SystemView/SEGGER_RTT_Conf.h deleted file mode 100755 index 10e6e0fb..00000000 --- a/src/Segger_SystemView/SEGGER_RTT_Conf.h +++ /dev/null @@ -1,269 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** ----------------------------------------------------------------------- -File : SEGGER_RTT_Conf.h -Purpose : Implementation of SEGGER real-time transfer (RTT) which - allows real-time communication on targets which support - debugger memory accesses while the CPU is running. -Revision: $Rev: 3892 $ ----------------------------END-OF-HEADER------------------------------ -*/ - -#ifndef SEGGER_RTT_CONF_H -#define SEGGER_RTT_CONF_H - -#ifdef __IAR_SYSTEMS_ICC__ - #include -#endif - -/********************************************************************* -* -* Defines, configurable -* -********************************************************************** -*/ - -#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (3) // Max. number of up-buffers (T->H) available on this target (Default: 3) -#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (3) // Max. number of down-buffers (H->T) available on this target (Default: 3) - -#define BUFFER_SIZE_UP (1024) // Size of the buffer for terminal output of target, up to host (Default: 1k) -#define BUFFER_SIZE_DOWN (16) // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16) - -#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64) - -#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP // Mode for pre-initialized terminal channel (buffer 0) - -// -// Target is not allowed to perform other RTT operations while string still has not been stored completely. -// Otherwise we would probably end up with a mixed string in the buffer. -// If using RTT from within interrupts, multiple tasks or multi processors, define the SEGGER_RTT_LOCK() and SEGGER_RTT_UNLOCK() function here. -// -// SEGGER_RTT_MAX_INTERRUPT_PRIORITY can be used in the sample lock routines on Cortex-M3/4. -// Make sure to mask all interrupts which can send RTT data, i.e. generate SystemView events, or cause task switches. -// When high-priority interrupts must not be masked while sending RTT data, SEGGER_RTT_MAX_INTERRUPT_PRIORITY needs to be adjusted accordingly. -// (Higher priority = lower priority number) -// Default value for embOS: 128u -// Default configuration in FreeRTOS: configMAX_SYSCALL_INTERRUPT_PRIORITY: ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) -// In case of doubt mask all interrupts: 1 << (8 - BASEPRI_PRIO_BITS) i.e. 1 << 5 when 3 bits are implemented in NVIC -// or define SEGGER_RTT_LOCK() to completely disable interrupts. -// - -#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) // Interrupt priority to lock on SEGGER_RTT_LOCK on Cortex-M3/4 (Default: 0x20) - -/********************************************************************* -* -* RTT lock configuration for SEGGER Embedded Studio, -* Rowley CrossStudio and GCC -*/ -#if (defined __SES_ARM) || (defined __CROSSWORKS_ARM) || (defined __GNUC__) - #ifdef __ARM_ARCH_6M__ - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - __asm volatile ("mrs %0, primask \n\t" \ - "mov r1, $1 \n\t" \ - "msr primask, r1 \n\t" \ - : "=r" (LockState) \ - : \ - : "r1" \ - ); - - #define SEGGER_RTT_UNLOCK() __asm volatile ("msr primask, %0 \n\t" \ - : \ - : "r" (LockState) \ - : \ - ); \ - } - - #elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)) - #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY - #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) - #endif - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - __asm volatile ("mrs %0, basepri \n\t" \ - "mov r1, %1 \n\t" \ - "msr basepri, r1 \n\t" \ - : "=r" (LockState) \ - : "i"(SEGGER_RTT_MAX_INTERRUPT_PRIORITY) \ - : "r1" \ - ); - - #define SEGGER_RTT_UNLOCK() __asm volatile ("msr basepri, %0 \n\t" \ - : \ - : "r" (LockState) \ - : \ - ); \ - } - - #elif defined(__ARM_ARCH_7A__) - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - __asm volatile ("mrs r1, CPSR \n\t" \ - "mov %0, r1 \n\t" \ - "orr r1, r1, #0xC0 \n\t" \ - "msr CPSR_c, r1 \n\t" \ - : "=r" (LockState) \ - : \ - : "r1" \ - ); - - #define SEGGER_RTT_UNLOCK() __asm volatile ("mov r0, %0 \n\t" \ - "mrs r1, CPSR \n\t" \ - "bic r1, r1, #0xC0 \n\t" \ - "and r0, r0, #0xC0 \n\t" \ - "orr r1, r1, r0 \n\t" \ - "msr CPSR_c, r1 \n\t" \ - : \ - : "r" (LockState) \ - : "r0", "r1" \ - ); \ - } -#else - #define SEGGER_RTT_LOCK() - #define SEGGER_RTT_UNLOCK() - #endif -#endif - -/********************************************************************* -* -* RTT lock configuration for IAR EWARM -*/ -#ifdef __ICCARM__ - #if (defined (__ARM6M__) && (__CORE__ == __ARM6M__)) - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - LockState = __get_PRIMASK(); \ - __set_PRIMASK(1); - - #define SEGGER_RTT_UNLOCK() __set_PRIMASK(LockState); \ - } - #elif ((defined (__ARM7EM__) && (__CORE__ == __ARM7EM__)) || (defined (__ARM7M__) && (__CORE__ == __ARM7M__))) - #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY - #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) - #endif - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - LockState = __get_BASEPRI(); \ - __set_BASEPRI(SEGGER_RTT_MAX_INTERRUPT_PRIORITY); - - #define SEGGER_RTT_UNLOCK() __set_BASEPRI(LockState); \ - } - #endif -#endif - -/********************************************************************* -* -* RTT lock configuration for IAR RX -*/ -#ifdef __ICCRX__ - #define SEGGER_RTT_LOCK() { \ - unsigned long LockState; \ - LockState = __get_interrupt_state(); \ - __disable_interrupt(); - - #define SEGGER_RTT_UNLOCK() __set_interrupt_state(LockState); \ - } -#endif - -/********************************************************************* -* -* RTT lock configuration for KEIL ARM -*/ -#ifdef __CC_ARM - #if (defined __TARGET_ARCH_6S_M) - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - register unsigned char PRIMASK __asm( "primask"); \ - LockState = PRIMASK; \ - PRIMASK = 1u; \ - __schedule_barrier(); - - #define SEGGER_RTT_UNLOCK() PRIMASK = LockState; \ - __schedule_barrier(); \ - } - #elif (defined(__TARGET_ARCH_7_M) || defined(__TARGET_ARCH_7E_M)) - #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY - #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) - #endif - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - register unsigned char BASEPRI __asm( "basepri"); \ - LockState = BASEPRI; \ - BASEPRI = SEGGER_RTT_MAX_INTERRUPT_PRIORITY; \ - __schedule_barrier(); - - #define SEGGER_RTT_UNLOCK() BASEPRI = LockState; \ - __schedule_barrier(); \ - } - #endif -#endif - -/********************************************************************* -* -* RTT lock configuration fallback -*/ -#ifndef SEGGER_RTT_LOCK - #define SEGGER_RTT_LOCK() // Lock RTT (nestable) (i.e. disable interrupts) -#endif - -#ifndef SEGGER_RTT_UNLOCK - #define SEGGER_RTT_UNLOCK() // Unlock RTT (nestable) (i.e. enable previous interrupt lock state) -#endif - -#endif -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW.c b/src/Segger_SystemView/SEGGER_SYSVIEW.c deleted file mode 100755 index 87869e7e..00000000 --- a/src/Segger_SystemView/SEGGER_SYSVIEW.c +++ /dev/null @@ -1,2763 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** --------------------------- END-OF-HEADER ----------------------------- - -File : SEGGER_SYSVIEW.c -Purpose : System visualization API implementation. -Revision: $Rev: 4039 $ - -Additional information: - Packet format: - Packets with IDs 0..23 are standard packets with known structure. - For efficiency, they do *NOT* contain a length field. - - - Packets with IDs 24..31 are standard packets with extendible - structure and contain a length field. - - - Packets with IDs >= 32 always contain a length field. - - - Packet IDs: - 0.. 31 : Standard packets, known by SystemViewer. - 32..1023 : OS-definable packets, described in a SystemView description file. - 1024..2047 : User-definable packets, described in a SystemView description file. - 2048..32767: Undefined. - - Data encoding: - Basic types (int, short, char, ...): - Basic types are encoded little endian with most-significant bit variant - encoding. - Each encoded byte contains 7 data bits [6:0] and the MSB continuation bit. - The continuation bit indicates whether the next byte belongs to the data - (bit set) or this is the last byte (bit clear). - The most significant bits of data are encoded first, proceeding to the - least significant bits in the final byte (little endian). - - Example encoding: - Data: 0x1F4 (500) - Encoded: 0xF4 (First 7 data bits 74 | Continuation bit) - 0x03 (Second 7 data bits 03, no continuation) - - Data: 0xFFFFFFFF - Encoded: 0xFF 0xFF 0xFF 0xFF 0x0F - - Data: 0xA2 (162), 0x03 (3), 0x7000 - Encoded: 0xA2 0x01 0x03 0x80 0xE0 0x01 - - Byte arrays and strings: - Byte arrays and strings are encoded as followed by the raw data. - NumBytes is encoded as a basic type with a theoretical maximum of 4G. - - Example encoding: - Data: "Hello World\0" (0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x00) - Encoded: 0x0B 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 - - Examples packets: - 01 F4 03 80 80 10 // Overflow packet. Data is a single U32. - This packet means: 500 packets lost, Timestamp is 0x40000 - - 02 0F 50 // ISR(15) Enter. Timestamp 80 (0x50) - - 03 20 // ISR Exit. Timestamp 32 (0x20) (Shortest possible packet.) - - Sample code for user defined Packets: - #define MY_ID 0x400 // Any value between 0x400 and 0x7FF - void SendMyPacket(unsigned Para0, unsigned Para1, const char* s) { - U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + MAX_STR_LEN + 1]; - U8* pPayload; - // - pPayload = SEGGER_SYSVIEW_PPREPARE_PACKET(aPacket); // Prepare the packet for SystemView - pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para0); // Add the first parameter to the packet - pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para1); // Add the second parameter to the packet - pPayload = SEGGER_SYSVIEW_EncodeString(pPayload, s, MAX_STR_LEN); // Add the string to the packet - // - SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, MY_ID); // Send the packet with EventId = MY_ID - } - - #define MY_ID_1 0x401 - void SendOnePara(unsigned Para0) { - SEGGER_SYSVIEW_RecordU32(MY_ID_1, Para0); - } - -*/ - -/********************************************************************* -* -* #include section -* -********************************************************************** -*/ - -#include "SEGGER_SYSVIEW_Int.h" -#include "SEGGER_RTT.h" -#include -#include -#include - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ -#if SEGGER_SYSVIEW_ID_SHIFT - #define SHRINK_ID(Id) (((Id) - _SYSVIEW_Globals.RAMBaseAddress) >> SEGGER_SYSVIEW_ID_SHIFT) -#else - #define SHRINK_ID(Id) ((Id) - _SYSVIEW_Globals.RAMBaseAddress) -#endif - -#if SEGGER_SYSVIEW_RTT_CHANNEL > 0 - #define CHANNEL_ID_UP SEGGER_SYSVIEW_RTT_CHANNEL - #define CHANNEL_ID_DOWN SEGGER_SYSVIEW_RTT_CHANNEL -#else - #define CHANNEL_ID_UP _SYSVIEW_Globals.UpChannel - #define CHANNEL_ID_DOWN _SYSVIEW_Globals.DownChannel -#endif - -/********************************************************************* -* -* Defines, configurable -* -********************************************************************** -*/ -// Timestamps may be less than full 32-bits, in which case we need to zero -// the unused bits to properly handle overflows. -// Note that this is a quite common scenario, as a 32-bit time such as -// SysTick might be scaled down to reduce bandwith -// or a 16-bit hardware time might be used. -#if SEGGER_SYSVIEW_TIMESTAMP_BITS < 32 // Eliminate unused bits in case hardware timestamps are less than 32 bits - #define MAKE_DELTA_32BIT(Delta) Delta <<= 32 - SEGGER_SYSVIEW_TIMESTAMP_BITS; \ - Delta >>= 32 - SEGGER_SYSVIEW_TIMESTAMP_BITS; -#else - #define MAKE_DELTA_32BIT(Delta) -#endif - - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ -#define ENABLE_STATE_OFF 0 -#define ENABLE_STATE_ON 1 -#define ENABLE_STATE_DROPPING 2 - -#define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0) -#define FORMAT_FLAG_PAD_ZERO (1u << 1) -#define FORMAT_FLAG_PRINT_SIGN (1u << 2) -#define FORMAT_FLAG_ALTERNATE (1u << 3) - -#define MODULE_EVENT_OFFSET (512) - -/********************************************************************* -* -* Types, local -* -********************************************************************** -*/ -typedef struct { - U8* pBuffer; - U8* pPayload; - U8* pPayloadStart; - U32 Options; - unsigned Cnt; -} SEGGER_SYSVIEW_PRINTF_DESC; - -typedef struct { - U8 EnableState; // 0: Disabled, 1: Enabled, (2: Dropping) - U8 UpChannel; - U8 RecursionCnt; - U32 SysFreq; - U32 CPUFreq; - U32 LastTxTimeStamp; - U32 RAMBaseAddress; -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) - U32 PacketCount; -#else - U32 DropCount; - U8 DownChannel; -#endif - U32 DisabledEvents; - const SEGGER_SYSVIEW_OS_API* pOSAPI; - SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC* pfSendSysDesc; -} SEGGER_SYSVIEW_GLOBALS; - -/********************************************************************* -* -* Function prototypes, required -* -********************************************************************** -*/ -static void _SendPacket(U8* pStartPacket, U8* pEndPacket, unsigned EventId); - -/********************************************************************* -* -* Static data -* -********************************************************************** -*/ -static const U8 _abSync[10] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -#ifdef SEGGER_SYSVIEW_SECTION - #if (defined __GNUC__) - __attribute__ ((section (SEGGER_SYSVIEW_SECTION))) static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; - #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) - __attribute__ ((section (SEGGER_SYSVIEW_SECTION))) static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms - #endif - #elif (defined __ICCARM__) || (defined __ICCRX__) - #pragma location=SEGGER_SYSVIEW_SECTION - static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; - #pragma location=SEGGER_SYSVIEW_SECTION - static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms - #elif (defined __CC_ARM__) - __attribute__ ((section (SEGGER_SYSVIEW_SECTION), zero_init)) static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; - #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) - __attribute__ ((section (SEGGER_SYSVIEW_SECTION), zero_init)) static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms - #endif - #else - static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; - #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) - static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms - #endif - #endif -#else - static char _UpBuffer [SEGGER_SYSVIEW_RTT_BUFFER_SIZE]; - #if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) - static char _DownBuffer[8]; // Small, fixed-size buffer, for back-channel comms - #endif -#endif - -static SEGGER_SYSVIEW_GLOBALS _SYSVIEW_Globals; - -static SEGGER_SYSVIEW_MODULE* _pFirstModule; -static U8 _NumModules; - -/********************************************************************* -* -* Static code -* -********************************************************************** -*/ - -#define ENCODE_U32(pDest, Value) { \ - U8* pSysviewPointer; \ - U32 SysViewData; \ - pSysviewPointer = pDest; \ - SysViewData = Value; \ - while(SysViewData > 0x7F) { \ - *pSysviewPointer++ = (U8)(SysViewData | 0x80); \ - SysViewData >>= 7; \ - }; \ - *pSysviewPointer++ = (U8)SysViewData; \ - pDest = pSysviewPointer; \ - }; - - - -#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 1) -static U8 _aPacket[SEGGER_SYSVIEW_MAX_PACKET_SIZE]; - -#define RECORD_START(PacketSize) SEGGER_SYSVIEW_LOCK(); \ - pPayloadStart = _PreparePacket(_aPacket); - -#define RECORD_END() SEGGER_SYSVIEW_UNLOCK() - -#else - -#define RECORD_START(PacketSize) U8 aPacket[(PacketSize)]; \ - pPayloadStart = _PreparePacket(aPacket); \ - -#define RECORD_END() - -#endif - -/********************************************************************* -* -* _EncodeData() -* -* Function description -* Encode a byte buffer in variable-length format. -* -* Parameters -* pPayload - Pointer to where string will be encoded. -* pSrc - Pointer to data buffer to be encoded. -* NumBytes - Number of bytes in the buffer to be encoded. -* -* Return value -* Pointer to the byte following the value, i.e. the first free -* byte in the payload and the next position to store payload -* content. -* -* Additional information -* The data is encoded as a count byte followed by the contents -* of the data buffer. -* Make sure NumBytes + 1 bytes are free for the payload. -*/ -static U8* _EncodeData(U8* pPayload, const char* pSrc, unsigned NumBytes) { - unsigned n; - // - n = 0; - *pPayload++ = NumBytes; - while (n < NumBytes) { - *pPayload++ = *pSrc++; - n++; - } - return pPayload; -} - -/********************************************************************* -* -* _EncodeStr() -* -* Function description -* Encode a string in variable-length format. -* -* Parameters -* pPayload - Pointer to where string will be encoded. -* pText - String to encode. -* Limit - Maximum number of characters to encode from string. -* -* Return value -* Pointer to the byte following the value, i.e. the first free -* byte in the payload and the next position to store payload -* content. -* -* Additional information -* The string is encoded as a count byte followed by the contents -* of the string. -* No more than 1 + Limit bytes will be encoded to the payload. -*/ -static U8 *_EncodeStr(U8 *pPayload, const char *pText, unsigned Limit) { - unsigned n; - unsigned Len; - // - // Compute string len - // - Len = 0; - while(*(pText + Len) != 0) { - Len++; - } - if (Len > Limit) { - Len = Limit; - } - // - // Write Len - // - if (Len < 255) { - *pPayload++ = Len; - } else { - *pPayload++ = 255; - *pPayload++ = (Len & 255); - *pPayload++ = ((Len >> 8) & 255); - } - // - // copy string - // - n = 0; - while (n < Len) { - *pPayload++ = *pText++; - n++; - } - return pPayload; -} - -/********************************************************************* -* -* _PreparePacket() -* -* Function description -* Prepare a SystemView event packet header. -* -* Parameters -* pPacket - Pointer to start of packet to initialize. -* -* Return value -* Pointer to first byte of packet payload. -* -* Additional information -* The payload length and evnetId are not initialized. -* PreparePacket only reserves space for them and they are -* computed and filled in by the sending function. -*/ -static U8* _PreparePacket(U8* pPacket) { - return pPacket + 4; -} - -/********************************************************************* -* -* _HandleIncomingPacket() -* -* Function description -* Read an incoming command from the down channel and process it. -* -* Additional information -* This function is called each time after sending a packet. -* Processing incoming packets is done asynchronous. SystemView might -* already have sent event packets after the host has sent a command. -*/ -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) -static void _HandleIncomingPacket(void) { - U8 Cmd; - int Status; - // - Status = SEGGER_RTT_ReadNoLock(CHANNEL_ID_DOWN, &Cmd, 1); - if (Status > 0) { - switch (Cmd) { - case SEGGER_SYSVIEW_COMMAND_ID_START: - SEGGER_SYSVIEW_Start(); - break; - case SEGGER_SYSVIEW_COMMAND_ID_STOP: - SEGGER_SYSVIEW_Stop(); - break; - case SEGGER_SYSVIEW_COMMAND_ID_GET_SYSTIME: - SEGGER_SYSVIEW_RecordSystime(); - break; - case SEGGER_SYSVIEW_COMMAND_ID_GET_TASKLIST: - SEGGER_SYSVIEW_SendTaskList(); - break; - case SEGGER_SYSVIEW_COMMAND_ID_GET_SYSDESC: - SEGGER_SYSVIEW_GetSysDesc(); - break; - case SEGGER_SYSVIEW_COMMAND_ID_GET_NUMMODULES: - SEGGER_SYSVIEW_SendNumModules(); - break; - case SEGGER_SYSVIEW_COMMAND_ID_GET_MODULEDESC: - SEGGER_SYSVIEW_SendModuleDescription(); - break; - case SEGGER_SYSVIEW_COMMAND_ID_GET_MODULE: - Status = SEGGER_RTT_ReadNoLock(CHANNEL_ID_DOWN, &Cmd, 1); - if (Status > 0) { - SEGGER_SYSVIEW_SendModule(Cmd); - } - break; - default: - if (Cmd >= 128) { // Unknown extended command. Dummy read its parameter. - SEGGER_RTT_ReadNoLock(CHANNEL_ID_DOWN, &Cmd, 1); - } - break; - } - } -} -#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) - -/********************************************************************* -* -* _TrySendOverflowPacket() -* -* Function description -* Try to transmit an SystemView Overflow packet containing the -* number of dropped packets. -* -* Additional information -* Format as follows: -* 01 Max. packet len is 1 + 5 + 5 = 11 -* -* Example packets sent -* 01 20 40 -* -* Return value -* !=0: Success, Message sent (stored in RTT-Buffer) -* ==0: Buffer full, Message *NOT* stored -* -*/ -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) -static int _TrySendOverflowPacket(void) { - U32 TimeStamp; - I32 Delta; - int Status; - U8 aPacket[11]; - U8* pPayload; - - aPacket[0] = SYSVIEW_EVTID_OVERFLOW; // 1 - pPayload = &aPacket[1]; - ENCODE_U32(pPayload, _SYSVIEW_Globals.DropCount); - // - // Compute time stamp delta and append it to packet. - // - TimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); - Delta = TimeStamp - _SYSVIEW_Globals.LastTxTimeStamp; - MAKE_DELTA_32BIT(Delta); - ENCODE_U32(pPayload, Delta); - // - // Try to store packet in RTT buffer and update time stamp when this was successful - // - Status = SEGGER_RTT_WriteSkipNoLock(CHANNEL_ID_UP, aPacket, pPayload - aPacket); - if (Status) { - _SYSVIEW_Globals.LastTxTimeStamp = TimeStamp; - _SYSVIEW_Globals.EnableState--; // EnableState has been 2, will be 1. Always. - } else { - _SYSVIEW_Globals.DropCount++; - } - // - return Status; -} -#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) - -/********************************************************************* -* -* _SendSyncInfo() -* -* Function description -* Send SystemView sync packet and system information in -* post mortem mode. -* -* Additional information -* Sync is 10 * 0x00 without timestamp -*/ -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) -static void _SendSyncInfo(void) { - // - // Add sync packet ( 10 * 0x00) - // Send system description - // Send system time - // Send task list - // Send module description - // Send module information - // - SEGGER_RTT_WriteWithOverwriteNoLock(CHANNEL_ID_UP, _abSync, 10); - SEGGER_SYSVIEW_RecordVoid(SYSVIEW_EVTID_TRACE_START); - { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, _SYSVIEW_Globals.SysFreq); - ENCODE_U32(pPayload, _SYSVIEW_Globals.CPUFreq); - ENCODE_U32(pPayload, _SYSVIEW_Globals.RAMBaseAddress); - ENCODE_U32(pPayload, SEGGER_SYSVIEW_ID_SHIFT); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_INIT); - RECORD_END(); - } - if (_SYSVIEW_Globals.pfSendSysDesc) { - _SYSVIEW_Globals.pfSendSysDesc(); - } - SEGGER_SYSVIEW_RecordSystime(); - SEGGER_SYSVIEW_SendTaskList(); - if (_NumModules > 0) { - SEGGER_SYSVIEW_SendNumModules(); - for (int n = 0; n < _NumModules; n++) { - SEGGER_SYSVIEW_SendModule(n); - } - SEGGER_SYSVIEW_SendModuleDescription(); - } -} -#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) - -/********************************************************************* -* -* _SendPacket() -* -* Function description -* Send a SystemView packet over RTT. RTT channel and mode are -* configured by macros when the SystemView component is initialized. -* This function takes care of maintaining the packet drop count -* and sending overflow packets when necessary. -* The packet must be passed without Id and Length because this -* function prepends it to the packet before transmission. -* -* Parameters -* pStartPacket - Pointer to start of packet payload. -* There must be at least 4 bytes free to prepend Id and Length. -* pEndPacket - Pointer to end of packet payload. -* EventId - Id of the event to send. -* -*/ -static void _SendPacket(U8* pStartPacket, U8* pEndPacket, unsigned EventId) { - unsigned NumBytes; - U32 TimeStamp; - U32 Delta; -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE != 1) - int Status; -#endif - -#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0) - SEGGER_SYSVIEW_LOCK(); -#endif - -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) - if (_SYSVIEW_Globals.EnableState == 0) { - goto SendDone; - } -#else - if (_SYSVIEW_Globals.EnableState == 1) { // Enabled, no dropped packets remaining - goto Send; - } - if (_SYSVIEW_Globals.EnableState == 0) { - goto SendDone; - } - // - // Handle buffer full situations: - // Have packets been dropped before because buffer was full? - // In this case try to send and overflow packet. - // - if (_SYSVIEW_Globals.EnableState == 2) { - _TrySendOverflowPacket(); - if (_SYSVIEW_Globals.EnableState != 1) { - goto SendDone; - } - } -Send: -#endif - // - // Check if event is disabled from being recorded. - // - if (EventId < 32) { - if (_SYSVIEW_Globals.DisabledEvents & ((U32)1u << EventId)) { - goto SendDone; - } - } - // - // Prepare actual packet. - // If it is a known packet, prepend eventId only, - // otherwise prepend packet length and eventId. - // - if (EventId < 24) { - *--pStartPacket = EventId; - } else { - NumBytes = pEndPacket - pStartPacket; - if (NumBytes > 127) { - *--pStartPacket = (NumBytes >> 7); - *--pStartPacket = NumBytes | 0x80; - } else { - *--pStartPacket = NumBytes; - } - if (EventId > 127) { - *--pStartPacket = (EventId >> 7); - *--pStartPacket = EventId | 0x80; - } else { - *--pStartPacket = EventId; - } - } - // - // Compute time stamp delta and append it to packet. - // - TimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); - Delta = TimeStamp - _SYSVIEW_Globals.LastTxTimeStamp; - MAKE_DELTA_32BIT(Delta); - ENCODE_U32(pEndPacket, Delta); -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) - // - // Store packet in RTT buffer by overwriting old data and update time stamp - // - SEGGER_RTT_WriteWithOverwriteNoLock(CHANNEL_ID_UP, pStartPacket, pEndPacket - pStartPacket); - _SYSVIEW_Globals.LastTxTimeStamp = TimeStamp; -#else - // - // Try to store packet in RTT buffer and update time stamp when this was successful - // - Status = SEGGER_RTT_WriteSkipNoLock(CHANNEL_ID_UP, pStartPacket, pEndPacket - pStartPacket); - if (Status) { - _SYSVIEW_Globals.LastTxTimeStamp = TimeStamp; - } else { - _SYSVIEW_Globals.EnableState++; // EnableState has been 1, will be 2. Always. - } -#endif - -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) - // - // Add sync and system information periodically if we are in post mortem mode - // - if (_SYSVIEW_Globals.RecursionCnt == 0) { // Avoid uncontrolled nesting. This way, this routine can call itself once, but no more often than that. - _SYSVIEW_Globals.RecursionCnt = 1; - if (_SYSVIEW_Globals.PacketCount++ & (1 << SEGGER_SYSVIEW_SYNC_PERIOD_SHIFT)) { - _SendSyncInfo(); - _SYSVIEW_Globals.PacketCount = 0; - } - _SYSVIEW_Globals.RecursionCnt = 0; - } -SendDone: - ; // Avoid "label at end of compound statement" error when using static buffer -#else -SendDone: - // - // Check if host is sending data which needs to be processed. - // Note that since this code is called for every packet, it is very time critical, so we do - // only what is really needed here, which is checking if there is any data - // - if (SEGGER_RTT_HASDATA(CHANNEL_ID_DOWN)) { - if (_SYSVIEW_Globals.RecursionCnt == 0) { // Avoid uncontrolled nesting. This way, this routine can call itself once, but no more often than that. - _SYSVIEW_Globals.RecursionCnt = 1; - _HandleIncomingPacket(); - _SYSVIEW_Globals.RecursionCnt = 0; - } - } -#endif - // -#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0) - SEGGER_SYSVIEW_UNLOCK(); // We are done. Unlock and return -#endif -} - -#ifndef SEGGER_SYSVIEW_EXCLUDE_PRINTF // Define in project to avoid warnings about variable parameter list -/********************************************************************* -* -* _APrintHost() -* -* Function description -* Prepares a string and its parameters to be formatted on the host. -* -* Parameters -* s Pointer to format string. -* Options Options to be sent to the host. -* pArguments Pointer to array of arguments for the format string. -* NumArguments Number of arguments in the array. -*/ -static void _APrintHost(const char* s, U32 Options, U32* pArguments, U32 NumArguments) { - U8* pPayload; - U8* pPayloadStart; - - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_ARGUMENTS * SEGGER_SYSVIEW_QUANTA_U32); - pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); - ENCODE_U32(pPayload, Options); - ENCODE_U32(pPayload, NumArguments); - while (NumArguments--) { - ENCODE_U32(pPayload, (*pArguments++)); - } - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); - RECORD_END(); -} - -/********************************************************************* -* -* _VPrintHost() -* -* Function description -* Prepares a string and its parameters to be formatted on the host. -* -* Parameters -* s Pointer to format string. -* Options Options to be sent to the host. -* pParamList Pointer to the list of arguments for the format string. -*/ -static void _VPrintHost(const char* s, U32 Options, va_list* pParamList) { - U32 aParas[SEGGER_SYSVIEW_MAX_ARGUMENTS]; - U32 NumArguments; - const char* p; - - p = s; - NumArguments = 0; - while (*p) { - if (*p == '%') { - aParas[NumArguments++] = va_arg(*pParamList, int); - if (NumArguments == SEGGER_SYSVIEW_MAX_ARGUMENTS) { - break; - } - } - p++; - } - _APrintHost(s, Options, aParas, NumArguments); -} - -/********************************************************************* -* -* _StoreChar() -* -* Function description -* Stores a character in the printf-buffer and sends the buffer when -* it is filled. -* -* Parameters -* p Pointer to the buffer description. -* c Character to be printed. -*/ -static void _StoreChar(SEGGER_SYSVIEW_PRINTF_DESC * p, char c) { - unsigned Cnt; - U8* pPayload; - U32 Options; - - Cnt = p->Cnt; - if ((Cnt + 1u) <= SEGGER_SYSVIEW_MAX_STRING_LEN) { - *(p->pPayload++) = c; - p->Cnt = Cnt + 1u; - } - // - // Write part of string, when the buffer is full - // - if (p->Cnt == SEGGER_SYSVIEW_MAX_STRING_LEN) { - *(p->pPayloadStart) = p->Cnt; - pPayload = p->pPayload; - Options = p->Options; - ENCODE_U32(pPayload, Options); - ENCODE_U32(pPayload, 0); - _SendPacket(p->pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); - p->pPayloadStart = _PreparePacket(p->pBuffer); - p->pPayload = p->pPayloadStart + 1u; - p->Cnt = 0u; - } -} - -/********************************************************************* -* -* _PrintUnsigned() -* -* Function description -* Print an unsigned integer with the given formatting into the -* formatted string. -* -* Parameters -* pBufferDesc Pointer to the buffer description. -* v Value to be printed. -* Base Base of the value. -* NumDigits Number of digits to be printed. -* FieldWidth Width of the printed field. -* FormatFlags Flags for formatting the value. -*/ -static void _PrintUnsigned(SEGGER_SYSVIEW_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) { - static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - unsigned Div; - unsigned Digit; - unsigned Number; - unsigned Width; - char c; - - Number = v; - Digit = 1u; - // - // Get actual field width - // - Width = 1u; - while (Number >= Base) { - Number = (Number / Base); - Width++; - } - if (NumDigits > Width) { - Width = NumDigits; - } - // - // Print leading chars if necessary - // - if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) { - if (FieldWidth != 0u) { - if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) { - c = '0'; - } else { - c = ' '; - } - while ((FieldWidth != 0u) && (Width < FieldWidth)) { - FieldWidth--; - _StoreChar(pBufferDesc, c); - } - } - } - // - // Compute Digit. - // Loop until Digit has the value of the highest digit required. - // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100. - // - while (1) { - if (NumDigits > 1u) { // User specified a min number of digits to print? => Make sure we loop at least that often, before checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned) - NumDigits--; - } else { - Div = v / Digit; - if (Div < Base) { // Is our divider big enough to extract the highest digit from value? => Done - break; - } - } - Digit *= Base; - } - // - // Output digits - // - do { - Div = v / Digit; - v -= Div * Digit; - _StoreChar(pBufferDesc, _aV2C[Div]); - Digit /= Base; - } while (Digit); - // - // Print trailing spaces if necessary - // - if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) { - if (FieldWidth != 0u) { - while ((FieldWidth != 0u) && (Width < FieldWidth)) { - FieldWidth--; - _StoreChar(pBufferDesc, ' '); - } - } - } -} - -/********************************************************************* -* -* _PrintInt() -* -* Function description -* Print a signed integer with the given formatting into the -* formatted string. -* -* Parameters -* pBufferDesc Pointer to the buffer description. -* v Value to be printed. -* Base Base of the value. -* NumDigits Number of digits to be printed. -* FieldWidth Width of the printed field. -* FormatFlags Flags for formatting the value. -*/ -static void _PrintInt(SEGGER_SYSVIEW_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) { - unsigned Width; - int Number; - - Number = (v < 0) ? -v : v; - - // - // Get actual field width - // - Width = 1u; - while (Number >= (int)Base) { - Number = (Number / (int)Base); - Width++; - } - if (NumDigits > Width) { - Width = NumDigits; - } - if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) { - FieldWidth--; - } - - // - // Print leading spaces if necessary - // - if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) { - if (FieldWidth != 0u) { - while ((FieldWidth != 0u) && (Width < FieldWidth)) { - FieldWidth--; - _StoreChar(pBufferDesc, ' '); - } - } - } - // - // Print sign if necessary - // - if (v < 0) { - v = -v; - _StoreChar(pBufferDesc, '-'); - } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) { - _StoreChar(pBufferDesc, '+'); - } else { - - } - // - // Print leading zeros if necessary - // - if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) { - if (FieldWidth != 0u) { - while ((FieldWidth != 0u) && (Width < FieldWidth)) { - FieldWidth--; - _StoreChar(pBufferDesc, '0'); - } - } - } - // - // Print number without sign - // - _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags); -} - -/********************************************************************* -* -* _VPrintTarget() -* -* Function description -* Stores a formatted string. -* This data is read by the host. -* -* Parameters -* sFormat Pointer to format string. -* Options Options to be sent to the host. -* pParamList Pointer to the list of arguments for the format string. -*/ -static void _VPrintTarget(const char* sFormat, U32 Options, va_list* pParamList) { - SEGGER_SYSVIEW_PRINTF_DESC BufferDesc; - char c; - int v; - unsigned NumDigits; - unsigned FormatFlags; - unsigned FieldWidth; - U8* pPayloadStart; -#if SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0 - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 1 + 2 * SEGGER_SYSVIEW_QUANTA_U32); - SEGGER_SYSVIEW_LOCK(); -#else - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 1 + 2 * SEGGER_SYSVIEW_QUANTA_U32); -#endif - -#if SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0 - BufferDesc.pBuffer = aPacket; -#else - BufferDesc.pBuffer = _aPacket; -#endif - BufferDesc.Cnt = 0u; - BufferDesc.pPayloadStart = pPayloadStart; - BufferDesc.pPayload = BufferDesc.pPayloadStart + 1u; - BufferDesc.Options = Options; - - do { - c = *sFormat; - sFormat++; - if (c == 0u) { - break; - } - if (c == '%') { - // - // Filter out flags - // - FormatFlags = 0u; - v = 1; - do { - c = *sFormat; - switch (c) { - case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break; - case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break; - case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break; - case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break; - default: v = 0; break; - } - } while (v); - // - // filter out field with - // - FieldWidth = 0u; - do { - c = *sFormat; - if ((c < '0') || (c > '9')) { - break; - } - sFormat++; - FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0'); - } while (1); - - // - // Filter out precision (number of digits to display) - // - NumDigits = 0u; - c = *sFormat; - if (c == '.') { - sFormat++; - do { - c = *sFormat; - if ((c < '0') || (c > '9')) { - break; - } - sFormat++; - NumDigits = NumDigits * 10u + ((unsigned)c - '0'); - } while (1); - } - // - // Filter out length modifier - // - c = *sFormat; - do { - if ((c == 'l') || (c == 'h')) { - c = *sFormat; - sFormat++; - } else { - break; - } - } while (1); - // - // Handle specifiers - // - switch (c) { - case 'c': { - char c0; - v = va_arg(*pParamList, int); - c0 = (char)v; - _StoreChar(&BufferDesc, c0); - break; - } - case 'd': - v = va_arg(*pParamList, int); - _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags); - break; - case 'u': - v = va_arg(*pParamList, int); - _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags); - break; - case 'x': - case 'X': - v = va_arg(*pParamList, int); - _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags); - break; - case 'p': - v = va_arg(*pParamList, int); - _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u); - break; - case '%': - _StoreChar(&BufferDesc, '%'); - break; - default: - break; - } - sFormat++; - } else { - _StoreChar(&BufferDesc, c); - } - } while (*sFormat); - - // - // Write remaining data, if any - // - if (BufferDesc.Cnt != 0u) { - *(BufferDesc.pPayloadStart) = BufferDesc.Cnt; - ENCODE_U32(BufferDesc.pPayload, BufferDesc.Options); - ENCODE_U32(BufferDesc.pPayload, 0); - _SendPacket(BufferDesc.pPayloadStart, BufferDesc.pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); - } -#if SEGGER_SYSVIEW_USE_STATIC_BUFFER == 0 - SEGGER_SYSVIEW_UNLOCK(); - RECORD_END(); -#else - RECORD_END(); -#endif -} -#endif // SEGGER_SYSVIEW_EXCLUDE_PRINTF - -/********************************************************************* -* -* Public functions -* -********************************************************************** -*/ - -/********************************************************************* -* -* SEGGER_SYSVIEW_Init() -* -* Function description -* Initializes the SYSVIEW module. -* Must be called before SystemViewer attaches to the system. -* -* Parameters -* SysFreq - Frequency of timestamp, i.e. CPU core clock frequency. -* CPUFreq - CPU core clock frequency. -* pOSAPI - Pointer to the API structure for OS-specific functions. -* pfSendSysDesc - Pointer to SendSysDesc callback function. -* -* Additional information -* This function initializes the RTT channel used to transport -* SEGGER SystemView packets. -* The channel is assigned the label "SysView" for client software -* to identify the SystemView channel. -* -* Notes -* The channel is configured by the macro SEGGER_SYSVIEW_RTT_CHANNEL. -*/ -void SEGGER_SYSVIEW_Init(U32 SysFreq, U32 CPUFreq, const SEGGER_SYSVIEW_OS_API *pOSAPI, SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC pfSendSysDesc) { -#ifdef SEGGER_RTT_SECTION - // - // Explicitly initialize the RTT Control Block if it is in its dedicated section. - // - SEGGER_RTT_Init(); -#endif -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) -#if SEGGER_SYSVIEW_RTT_CHANNEL > 0 - SEGGER_RTT_ConfigUpBuffer(SEGGER_SYSVIEW_RTT_CHANNEL, "SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); -#else - _SYSVIEW_Globals.UpChannel = SEGGER_RTT_AllocUpBuffer ("SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); -#endif - _SYSVIEW_Globals.RAMBaseAddress = SEGGER_SYSVIEW_ID_BASE; - _SYSVIEW_Globals.LastTxTimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); - _SYSVIEW_Globals.pOSAPI = pOSAPI; - _SYSVIEW_Globals.SysFreq = SysFreq; - _SYSVIEW_Globals.CPUFreq = CPUFreq; - _SYSVIEW_Globals.pfSendSysDesc = pfSendSysDesc; - _SYSVIEW_Globals.EnableState = 0; - _SYSVIEW_Globals.PacketCount = 0; -#else // (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) -#if SEGGER_SYSVIEW_RTT_CHANNEL > 0 - SEGGER_RTT_ConfigUpBuffer (SEGGER_SYSVIEW_RTT_CHANNEL, "SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); - SEGGER_RTT_ConfigDownBuffer (SEGGER_SYSVIEW_RTT_CHANNEL, "SysView", &_DownBuffer[0], sizeof(_DownBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); -#else - _SYSVIEW_Globals.UpChannel = SEGGER_RTT_AllocUpBuffer ("SysView", &_UpBuffer[0], sizeof(_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); - // - // TODO: Use SEGGER_RTT_AllocDownBuffer when SystemViewer is able to handle another Down Channel than Up Channel. - // - _SYSVIEW_Globals.DownChannel = _SYSVIEW_Globals.UpChannel; - SEGGER_RTT_ConfigDownBuffer (_SYSVIEW_Globals.DownChannel, "SysView", &_DownBuffer[0], sizeof(_DownBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); -#endif - _SYSVIEW_Globals.RAMBaseAddress = SEGGER_SYSVIEW_ID_BASE; - _SYSVIEW_Globals.LastTxTimeStamp = SEGGER_SYSVIEW_GET_TIMESTAMP(); - _SYSVIEW_Globals.pOSAPI = pOSAPI; - _SYSVIEW_Globals.SysFreq = SysFreq; - _SYSVIEW_Globals.CPUFreq = CPUFreq; - _SYSVIEW_Globals.pfSendSysDesc = pfSendSysDesc; - _SYSVIEW_Globals.EnableState = 0; -#endif // (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SetRAMBase() -* -* Function description -* Sets the RAM base address, which is subtracted from IDs in order -* to save bandwidth. -* -* Parameters -* RAMBaseAddress - Lowest RAM Address. (i.e. 0x20000000 on most Cortex-M) -*/ -void SEGGER_SYSVIEW_SetRAMBase(U32 RAMBaseAddress) { - _SYSVIEW_Globals.RAMBaseAddress = RAMBaseAddress; -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordVoid() -* -* Function description -* Formats and sends a SystemView packet with an empty payload. -* -* Parameters -* EventID - SystemView event ID. -*/ -void SEGGER_SYSVIEW_RecordVoid(unsigned EventID) { - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); - // - _SendPacket(pPayloadStart, pPayloadStart, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32() -* -* Function description -* Formats and sends a SystemView packet containing a single U32 -* parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Value - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32(unsigned EventID, U32 Value) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Value); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x2() -* -* Function description -* Formats and sends a SystemView packet containing 2 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x2(unsigned EventID, U32 Para0, U32 Para1) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x3() -* -* Function description -* Formats and sends a SystemView packet containing 3 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x3(unsigned EventID, U32 Para0, U32 Para1, U32 Para2) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 3 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x4() -* -* Function description -* Formats and sends a SystemView packet containing 4 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -* Para3 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x4(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - ENCODE_U32(pPayload, Para3); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x5() -* -* Function description -* Formats and sends a SystemView packet containing 5 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -* Para3 - The 32-bit parameter encoded to SystemView packet payload. -* Para4 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x5(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 5 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - ENCODE_U32(pPayload, Para3); - ENCODE_U32(pPayload, Para4); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x6() -* -* Function description -* Formats and sends a SystemView packet containing 6 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -* Para3 - The 32-bit parameter encoded to SystemView packet payload. -* Para4 - The 32-bit parameter encoded to SystemView packet payload. -* Para5 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x6(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 6 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - ENCODE_U32(pPayload, Para3); - ENCODE_U32(pPayload, Para4); - ENCODE_U32(pPayload, Para5); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x7() -* -* Function description -* Formats and sends a SystemView packet containing 7 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -* Para3 - The 32-bit parameter encoded to SystemView packet payload. -* Para4 - The 32-bit parameter encoded to SystemView packet payload. -* Para5 - The 32-bit parameter encoded to SystemView packet payload. -* Para6 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x7(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 7 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - ENCODE_U32(pPayload, Para3); - ENCODE_U32(pPayload, Para4); - ENCODE_U32(pPayload, Para5); - ENCODE_U32(pPayload, Para6); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x8() -* -* Function description -* Formats and sends a SystemView packet containing 8 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -* Para3 - The 32-bit parameter encoded to SystemView packet payload. -* Para4 - The 32-bit parameter encoded to SystemView packet payload. -* Para5 - The 32-bit parameter encoded to SystemView packet payload. -* Para6 - The 32-bit parameter encoded to SystemView packet payload. -* Para7 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x8(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 8 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - ENCODE_U32(pPayload, Para3); - ENCODE_U32(pPayload, Para4); - ENCODE_U32(pPayload, Para5); - ENCODE_U32(pPayload, Para6); - ENCODE_U32(pPayload, Para7); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x9() -* -* Function description -* Formats and sends a SystemView packet containing 9 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -* Para3 - The 32-bit parameter encoded to SystemView packet payload. -* Para4 - The 32-bit parameter encoded to SystemView packet payload. -* Para5 - The 32-bit parameter encoded to SystemView packet payload. -* Para6 - The 32-bit parameter encoded to SystemView packet payload. -* Para7 - The 32-bit parameter encoded to SystemView packet payload. -* Para8 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x9(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 9 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - ENCODE_U32(pPayload, Para3); - ENCODE_U32(pPayload, Para4); - ENCODE_U32(pPayload, Para5); - ENCODE_U32(pPayload, Para6); - ENCODE_U32(pPayload, Para7); - ENCODE_U32(pPayload, Para8); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordU32x10() -* -* Function description -* Formats and sends a SystemView packet containing 10 U32 parameter payload. -* -* Parameters -* EventID - SystemView event ID. -* Para0 - The 32-bit parameter encoded to SystemView packet payload. -* Para1 - The 32-bit parameter encoded to SystemView packet payload. -* Para2 - The 32-bit parameter encoded to SystemView packet payload. -* Para3 - The 32-bit parameter encoded to SystemView packet payload. -* Para4 - The 32-bit parameter encoded to SystemView packet payload. -* Para5 - The 32-bit parameter encoded to SystemView packet payload. -* Para6 - The 32-bit parameter encoded to SystemView packet payload. -* Para7 - The 32-bit parameter encoded to SystemView packet payload. -* Para8 - The 32-bit parameter encoded to SystemView packet payload. -* Para9 - The 32-bit parameter encoded to SystemView packet payload. -*/ -void SEGGER_SYSVIEW_RecordU32x10(unsigned EventID, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8, U32 Para9) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 10 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, Para0); - ENCODE_U32(pPayload, Para1); - ENCODE_U32(pPayload, Para2); - ENCODE_U32(pPayload, Para3); - ENCODE_U32(pPayload, Para4); - ENCODE_U32(pPayload, Para5); - ENCODE_U32(pPayload, Para6); - ENCODE_U32(pPayload, Para7); - ENCODE_U32(pPayload, Para8); - ENCODE_U32(pPayload, Para9); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordString() -* -* Function description -* Formats and sends a SystemView packet containing a string. -* -* Parameters -* EventID - SystemView event ID. -* pString - The string to be sent in the SystemView packet payload. -* -* Additional information -* The string is encoded as a count byte followed by the contents -* of the string. -* No more than SEGGER_SYSVIEW_MAX_STRING_LEN bytes will be encoded to the payload. -*/ -void SEGGER_SYSVIEW_RecordString(unsigned EventID, const char* pString) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = _EncodeStr(pPayloadStart, pString, SEGGER_SYSVIEW_MAX_STRING_LEN); - _SendPacket(pPayloadStart, pPayload, EventID); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_Start() -* -* Function description -* Start recording SystemView events. -* This function is triggered by the host application. -* -* Additional information -* This function enables transmission of SystemView packets recorded -* by subsequent trace calls and records a SystemView Start event. -* -* As part of start, a SystemView Init packet is sent, containing the system -* frequency. The list of current tasks, the current system time and the -* system description string is sent, too. -* -* Notes -* SEGGER_SYSVIEW_Start and SEGGER_SYSVIEW_Stop do not nest. -*/ -void SEGGER_SYSVIEW_Start(void) { - if (_SYSVIEW_Globals.EnableState == 0) { - _SYSVIEW_Globals.EnableState = 1; -#if (SEGGER_SYSVIEW_POST_MORTEM_MODE == 1) - _SendSyncInfo(); -#else - SEGGER_SYSVIEW_LOCK(); - SEGGER_RTT_WriteSkipNoLock(CHANNEL_ID_UP, _abSync, 10); - SEGGER_SYSVIEW_UNLOCK(); - SEGGER_SYSVIEW_RecordVoid(SYSVIEW_EVTID_TRACE_START); - { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, _SYSVIEW_Globals.SysFreq); - ENCODE_U32(pPayload, _SYSVIEW_Globals.CPUFreq); - ENCODE_U32(pPayload, _SYSVIEW_Globals.RAMBaseAddress); - ENCODE_U32(pPayload, SEGGER_SYSVIEW_ID_SHIFT); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_INIT); - RECORD_END(); - } - if (_SYSVIEW_Globals.pfSendSysDesc) { - _SYSVIEW_Globals.pfSendSysDesc(); - } - SEGGER_SYSVIEW_RecordSystime(); - SEGGER_SYSVIEW_SendTaskList(); - SEGGER_SYSVIEW_SendNumModules(); -#endif - } -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_Stop() -* -* Function description -* Stop recording SystemView events. -* -* Additional information -* This function disables transmission of SystemView packets recorded -* by subsequent trace calls. If transmission is enabled when -* this function is called, a single SystemView Stop event is recorded -* to the trace, send, and then trace transmission is halted. -*/ -void SEGGER_SYSVIEW_Stop(void) { - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); - // - if (_SYSVIEW_Globals.EnableState) { - _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_TRACE_STOP); - _SYSVIEW_Globals.EnableState = 0; - } - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_GetSysDesc() -* -* Function description -* Triggers a send of the system information and description. -* -*/ -void SEGGER_SYSVIEW_GetSysDesc(void) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, _SYSVIEW_Globals.SysFreq); - ENCODE_U32(pPayload, _SYSVIEW_Globals.CPUFreq); - ENCODE_U32(pPayload, _SYSVIEW_Globals.RAMBaseAddress); - ENCODE_U32(pPayload, SEGGER_SYSVIEW_ID_SHIFT); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_INIT); - RECORD_END(); - if (_SYSVIEW_Globals.pfSendSysDesc) { - _SYSVIEW_Globals.pfSendSysDesc(); - } -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SendTaskInfo() -* -* Function description -* Send a Task Info Packet, containing TaskId for identification, -* task priority and task name. -* -* Parameters -* pInfo - Pointer to task information to send. -*/ -void SEGGER_SYSVIEW_SendTaskInfo(const SEGGER_SYSVIEW_TASKINFO *pInfo) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32 + 1 + 32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, SHRINK_ID(pInfo->TaskID)); - ENCODE_U32(pPayload, pInfo->Prio); - pPayload = _EncodeStr(pPayload, pInfo->sName, 32); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_INFO); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, SHRINK_ID(pInfo->TaskID)); - ENCODE_U32(pPayload, pInfo->StackBase); - ENCODE_U32(pPayload, pInfo->StackSize); - ENCODE_U32(pPayload, 0); // Stack End, future use - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_STACK_INFO); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SendTaskList() -* -* Function description -* Send all tasks descriptors to the host. -*/ -void SEGGER_SYSVIEW_SendTaskList(void) { - if (_SYSVIEW_Globals.pOSAPI && _SYSVIEW_Globals.pOSAPI->pfSendTaskList) { - _SYSVIEW_Globals.pOSAPI->pfSendTaskList(); - } -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SendSysDesc() -* -* Function description -* Send the system description string to the host. -* The system description is used by SystemViewer to identify the -* current application and handle events accordingly. -* -* Parameters -* sSysDesc - Pointer to the 0-terminated system description string. -* -* Additional information -* One system description string may not exceed SEGGER_SYSVIEW_MAX_STRING_LEN characters. -* -* The Following items can be described in a system description string. -* Each item is identified by its identifier, followed by '=' and the value. -* Items are separated by ','. -*/ -void SEGGER_SYSVIEW_SendSysDesc(const char *sSysDesc) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = _EncodeStr(pPayloadStart, sSysDesc, SEGGER_SYSVIEW_MAX_STRING_LEN); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_SYSDESC); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordSystime() -* -* Function description -* Formats and sends a SystemView Systime containing a single U64 or U32 -* parameter payload. -*/ -void SEGGER_SYSVIEW_RecordSystime(void) { - U64 Systime; - - if (_SYSVIEW_Globals.pOSAPI && _SYSVIEW_Globals.pOSAPI->pfGetTime) { - Systime = _SYSVIEW_Globals.pOSAPI->pfGetTime(); - SEGGER_SYSVIEW_RecordU32x2(SYSVIEW_EVTID_SYSTIME_US, - (U32)(Systime), - (U32)(Systime >> 32)); - } else { - SEGGER_SYSVIEW_RecordU32(SYSVIEW_EVTID_SYSTIME_CYCLES, SEGGER_SYSVIEW_GET_TIMESTAMP()); - } -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordEnterISR() -* -* Function description -* Format and send an ISR entry event. -* -* Additional information -* Example packets sent -* 02 0F 50 // ISR(15) Enter. Timestamp is 80 (0x50) -*/ -void SEGGER_SYSVIEW_RecordEnterISR(void) { - unsigned v; - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - v = SEGGER_SYSVIEW_GET_INTERRUPT_ID(); - ENCODE_U32(pPayload, v); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_ISR_ENTER); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordExitISR() -* -* Function description -* Format and send an ISR exit event. -* -* Additional information -* Format as follows: -* 03 // Max. packet len is 6 -* -* Example packets sent -* 03 20 // ISR Exit. Timestamp is 32 (0x20) -*/ -void SEGGER_SYSVIEW_RecordExitISR(void) { - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); - // - _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_ISR_EXIT); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordExitISRToScheduler() -* -* Function description -* Format and send an ISR exit into scheduler event. -* -* Additional information -* Format as follows: -* 18 // Max. packet len is 6 -* -* Example packets sent -* 18 20 // ISR Exit to Scheduler. Timestamp is 32 (0x20) -*/ -void SEGGER_SYSVIEW_RecordExitISRToScheduler(void) { - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); - // - _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_ISR_TO_SCHEDULER); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordEnterTimer() -* -* Function description -* Format and send a Timer entry event. -* -* Parameters -* TimerId - Id of the timer which starts. -*/ -void SEGGER_SYSVIEW_RecordEnterTimer(U32 TimerId) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, SHRINK_ID(TimerId)); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TIMER_ENTER); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordExitTimer() -* -* Function description -* Format and send a Timer exit event. -*/ -void SEGGER_SYSVIEW_RecordExitTimer(void) { - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); - // - _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_TIMER_EXIT); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordEndCall() -* -* Function description -* Format and send an End API Call event without return value. -* -* Parameters -* EventID - Id of API function which ends. -*/ -void SEGGER_SYSVIEW_RecordEndCall(unsigned EventID) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, EventID); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_END_CALL); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordEndCallU32() -* -* Function description -* Format and send an End API Call event with return value. -* -* Parameters -* EventID - Id of API function which ends. -* Para0 - Return value which will be returned by the API function. -*/ -void SEGGER_SYSVIEW_RecordEndCallU32(unsigned EventID, U32 Para0) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, EventID); - ENCODE_U32(pPayload, Para0); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_END_CALL); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnIdle() -* -* Function description -* Record an Idle event. -*/ -void SEGGER_SYSVIEW_OnIdle(void) { - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); - // - _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_IDLE); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnTaskCreate() -* -* Function description -* Record a Task Create event. The Task Create event corresponds -* to creating a task in the OS. -* -* Parameters -* TaskId - Task ID of created task. -*/ -void SEGGER_SYSVIEW_OnTaskCreate(unsigned TaskId) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - TaskId = SHRINK_ID(TaskId); - ENCODE_U32(pPayload, TaskId); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_CREATE); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnTaskTerminate() -* -* Function description -* Record a Task termination event. -* The Task termination event corresponds to terminating a task in -* the OS. If the TaskId is the currently active task, -* SEGGER_SYSVIEW_OnTaskStopExec may be used, either. -* -* Parameters -* TaskId - Task ID of terminated task. -*/ -void SEGGER_SYSVIEW_OnTaskTerminate(unsigned TaskId) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - TaskId = SHRINK_ID(TaskId); - ENCODE_U32(pPayload, TaskId); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_TERMINATE); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnTaskStartExec() -* -* Function description -* Record a Task Start Execution event. The Task Start event -* corresponds to when a task has started to execute rather than -* when it is ready to execute. -* -* Parameters -* TaskId - Task ID of task that started to execute. -*/ -void SEGGER_SYSVIEW_OnTaskStartExec(unsigned TaskId) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - TaskId = SHRINK_ID(TaskId); - ENCODE_U32(pPayload, TaskId); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_START_EXEC); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnTaskStopExec() -* -* Function description -* Record a Task Stop Execution event. The Task Stop event -* corresponds to when a task stops executing and terminates. -*/ -void SEGGER_SYSVIEW_OnTaskStopExec(void) { - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE); - // - _SendPacket(pPayloadStart, pPayloadStart, SYSVIEW_EVTID_TASK_STOP_EXEC); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnTaskStartReady() -* -* Function description -* Record a Task Start Ready event. -* -* Parameters -* TaskId - Task ID of task that started to execute. -*/ -void SEGGER_SYSVIEW_OnTaskStartReady(unsigned TaskId) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - TaskId = SHRINK_ID(TaskId); - ENCODE_U32(pPayload, TaskId); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_START_READY); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnTaskStopReady() -* -* Function description -* Record a Task Stop Ready event. -* -* Parameters -* TaskId - Task ID of task that completed execution. -* Cause - Reason for task to stop (i.e. Idle/Sleep) -*/ -void SEGGER_SYSVIEW_OnTaskStopReady(unsigned TaskId, unsigned Cause) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - TaskId = SHRINK_ID(TaskId); - ENCODE_U32(pPayload, TaskId); - ENCODE_U32(pPayload, Cause); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_TASK_STOP_READY); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnUserStart() -* -* Function description -* Send a user event start, such as start of a subroutine for profiling. -* -* Parameters -* UserId - User defined ID for the event. -*/ -void SEGGER_SYSVIEW_OnUserStart(unsigned UserId) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, UserId); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_USER_START); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_OnUserStop() -* -* Function description -* Send a user event stop, such as return of a subroutine for profiling. -* -* Parameters -* UserId - User defined ID for the event. -*/ -void SEGGER_SYSVIEW_OnUserStop(unsigned UserId) { - U8 * pPayload; - U8 * pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, UserId); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_USER_STOP); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_NameResource() -* -* Function description -* Send the name of a resource to be displayed in SystemViewer. -* -* Parameters -* ResourceId - Id of the resource to be named. i.e. its address. -* sName - Pointer to the resource name. (Max. SEGGER_SYSVIEW_MAX_STRING_LEN Bytes) -*/ -void SEGGER_SYSVIEW_NameResource(U32 ResourceId, const char* sName) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_QUANTA_U32 + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = pPayloadStart; - ENCODE_U32(pPayload, SHRINK_ID(ResourceId)); - pPayload = _EncodeStr(pPayload, sName, SEGGER_SYSVIEW_MAX_STRING_LEN); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_NAME_RESOURCE); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SendPacket() -* -* Function description -* Send an event packet. -* -* Parameters -* pPacket - Pointer to the start of the packet. -* pPayloadEnd - Pointer to the end of the payload. -* Make sure there are at least 5 bytes free after the payload. -* EventId - Id of the event packet. -* -* Return value -* !=0: Success, Message sent. -* ==0: Buffer full, Message *NOT* sent. -*/ -int SEGGER_SYSVIEW_SendPacket(U8* pPacket, U8* pPayloadEnd, unsigned EventId) { -#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 1) - SEGGER_SYSVIEW_LOCK(); -#endif - _SendPacket(pPacket + 4, pPayloadEnd, EventId); -#if (SEGGER_SYSVIEW_USE_STATIC_BUFFER == 1) - SEGGER_SYSVIEW_UNLOCK(); -#endif - return 0; -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_EncodeU32() -* -* Function description -* Encode a U32 in variable-length format. -* -* Parameters -* pPayload - Pointer to where U32 will be encoded. -* Value - The 32-bit value to be encoded. -* -* Return value -* Pointer to the byte following the value, i.e. the first free -* byte in the payload and the next position to store payload -* content. -*/ -U8* SEGGER_SYSVIEW_EncodeU32(U8* pPayload, unsigned Value) { - ENCODE_U32(pPayload, Value); - return pPayload; -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_EncodeString() -* -* Function description -* Encode a string in variable-length format. -* -* Parameters -* pPayload - Pointer to where string will be encoded. -* s - String to encode. -* MaxLen - Maximum number of characters to encode from string. -* -* Return value -* Pointer to the byte following the value, i.e. the first free -* byte in the payload and the next position to store payload -* content. -* -* Additional information -* The string is encoded as a count byte followed by the contents -* of the string. -* No more than 1 + MaxLen bytes will be encoded to the payload. -*/ -U8* SEGGER_SYSVIEW_EncodeString(U8* pPayload, const char* s, unsigned MaxLen) { - return _EncodeStr(pPayload, s, MaxLen); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_EncodeData() -* -* Function description -* Encode a byte buffer in variable-length format. -* -* Parameters -* pPayload - Pointer to where string will be encoded. -* pSrc - Pointer to data buffer to be encoded. -* NumBytes - Number of bytes in the buffer to be encoded. -* -* Return value -* Pointer to the byte following the value, i.e. the first free -* byte in the payload and the next position to store payload -* content. -* -* Additional information -* The data is encoded as a count byte followed by the contents -* of the data buffer. -* Make sure NumBytes + 1 bytes are free for the payload. -*/ -U8* SEGGER_SYSVIEW_EncodeData(U8 *pPayload, const char* pSrc, unsigned NumBytes) { - return _EncodeData(pPayload, pSrc, NumBytes); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_EncodeId() -* -* Function description -* Encode a 32-bit Id in shrunken variable-length format. -* -* Parameters -* pPayload - Pointer to where the Id will be encoded. -* Id - The 32-bit value to be encoded. -* -* Return value -* Pointer to the byte following the value, i.e. the first free -* byte in the payload and the next position to store payload -* content. -* -* Additional information -* The parameters to shrink an Id can be configured in -* SEGGER_SYSVIEW_Conf.h and via SEGGER_SYSVIEW_SetRAMBase(). -* SEGGER_SYSVIEW_ID_BASE: Lowest Id reported by the application. -* (i.e. 0x20000000 when all Ids are an address in this RAM) -* SEGGER_SYSVIEW_ID_SHIFT: Number of bits to shift the Id to -* save bandwidth. (i.e. 2 when Ids are 4 byte aligned) -*/ -U8* SEGGER_SYSVIEW_EncodeId(U8* pPayload, unsigned Id) { - Id = SHRINK_ID(Id); - ENCODE_U32(pPayload, Id); - return pPayload; -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_ShrinkId() -* -* Function description -* Get the shrunken value of an Id for further processing like in -* SEGGER_SYSVIEW_NameResource(). -* -* Parameters -* Id - The 32-bit value to be shrunken. -* -* Return value -* Shrunken Id. -* -* Additional information -* The parameters to shrink an Id can be configured in -* SEGGER_SYSVIEW_Conf.h and via SEGGER_SYSVIEW_SetRAMBase(). -* SEGGER_SYSVIEW_ID_BASE: Lowest Id reported by the application. -* (i.e. 0x20000000 when all Ids are an address in this RAM) -* SEGGER_SYSVIEW_ID_SHIFT: Number of bits to shift the Id to -* save bandwidth. (i.e. 2 when Ids are 4 byte aligned) -*/ -U32 SEGGER_SYSVIEW_ShrinkId(U32 Id) { - return SHRINK_ID(Id); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RegisterModule() -* -* Function description -* Register a middleware module for recording its events. -* -* Parameters -* pModule - The middleware module information. -* -* Additional information -* SEGGER_SYSVIEW_MODULE elements: -* sDescription - Pointer to a string containing the module name and optionally the module event description. -* NumEvents - Number of events the module wants to register. -* EventOffset - Offset to be added to the event Ids. Out parameter, set by this function. Do not modify after calling this function. -* pfSendModuleDesc - Callback function pointer to send more detailed module description to SystemViewer. -* pNext - Pointer to next registered module. Out parameter, set by this function. Do not modify after calling this function. -*/ -void SEGGER_SYSVIEW_RegisterModule(SEGGER_SYSVIEW_MODULE* pModule) { - SEGGER_SYSVIEW_LOCK(); - if (_pFirstModule == 0) { - // - // No module registered, yet. - // Start list with new module. - // EventOffset is the base offset for modules - // - pModule->EventOffset = MODULE_EVENT_OFFSET; - pModule->pNext = 0; - _pFirstModule = pModule; - _NumModules = 1; - } else { - // - // Registreded module(s) present. - // Prepend new module in list. - // EventOffset set from number of events and offset of previous module. - // - pModule->EventOffset = _pFirstModule->EventOffset + _pFirstModule->NumEvents; - pModule->pNext = _pFirstModule; - _pFirstModule = pModule; - _NumModules++; - } - SEGGER_SYSVIEW_SendModule(0); - if (pModule->pfSendModuleDesc) { - pModule->pfSendModuleDesc(); - } - SEGGER_SYSVIEW_UNLOCK(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_RecordModuleDescription() -* -* Function description -* Sends detailed information of a registered module to the host. -* -* Parameters -* pModule - Pointer to the described module. -* sDescription - Pointer to a description string. -*/ -void SEGGER_SYSVIEW_RecordModuleDescription(const SEGGER_SYSVIEW_MODULE* pModule, const char* sDescription) { - U8 ModuleId; - SEGGER_SYSVIEW_MODULE* p; - - p = _pFirstModule; - ModuleId = 0; - do { - if (p == pModule) { - break; - } - ModuleId++; - p = p->pNext; - } while (p); - { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = pPayloadStart; - // - // Send module description - // Send event offset and number of events - // - ENCODE_U32(pPayload, ModuleId); - ENCODE_U32(pPayload, (pModule->EventOffset)); - pPayload = _EncodeStr(pPayload, sDescription, SEGGER_SYSVIEW_MAX_STRING_LEN); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_MODULEDESC); - RECORD_END(); - } -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SendModule() -* -* Function description -* Sends the information of a registered module to the host. -* -* Parameters -* ModuleId - Id of the requested module. -*/ -void SEGGER_SYSVIEW_SendModule(U8 ModuleId) { - SEGGER_SYSVIEW_MODULE* pModule; - U32 n; - - if (_pFirstModule != 0) { - pModule = _pFirstModule; - for (n = 0; n < ModuleId; n++) { - pModule = pModule->pNext; - if (pModule == 0) { - break; - } - } - if (pModule != 0) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + 1 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = pPayloadStart; - // - // Send module description - // Send event offset and number of events - // - ENCODE_U32(pPayload, ModuleId); - ENCODE_U32(pPayload, (pModule->EventOffset)); - pPayload = _EncodeStr(pPayload, pModule->sModule, SEGGER_SYSVIEW_MAX_STRING_LEN); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_MODULEDESC); - RECORD_END(); - } - } -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SendModuleDescription() -* -* Function description -* Triggers a send of the registered module descriptions. -* -*/ -void SEGGER_SYSVIEW_SendModuleDescription(void) { - SEGGER_SYSVIEW_MODULE* pModule; - - if (_pFirstModule != 0) { - pModule = _pFirstModule; - do { - if (pModule->pfSendModuleDesc) { - pModule->pfSendModuleDesc(); - } - pModule = pModule->pNext; - } while (pModule); - } -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_SendNumModules() -* -* Function description -* Send the number of registered modules to the host. -*/ -void SEGGER_SYSVIEW_SendNumModules(void) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2*SEGGER_SYSVIEW_QUANTA_U32); - pPayload = pPayloadStart; - ENCODE_U32(pPayload, _NumModules); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_NUMMODULES); - RECORD_END(); -} - -#ifndef SEGGER_SYSVIEW_EXCLUDE_PRINTF // Define in project to avoid warnings about variable parameter list - -/********************************************************************* -* -* SEGGER_SYSVIEW_PrintfHostEx() -* -* Function description -* Print a string which is formatted on the host by SystemViewer -* with Additional information. -* -* Parameters -* s - String to be formatted. -* Options - Options for the string. i.e. Log level. -* -* Additional information -* All format arguments are treated as 32-bit scalar values. -*/ -void SEGGER_SYSVIEW_PrintfHostEx(const char* s, U32 Options, ...) { - va_list ParamList; - - va_start(ParamList, Options); - _VPrintHost(s, Options, &ParamList); - va_end(ParamList); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_PrintfHost() -* -* Function description -* Print a string which is formatted on the host by SystemViewer. -* -* Parameters -* s - String to be formatted. -* -* Additional information -* All format arguments are treated as 32-bit scalar values. -*/ -void SEGGER_SYSVIEW_PrintfHost(const char* s, ...) { - va_list ParamList; - - va_start(ParamList, s); - _VPrintHost(s, SEGGER_SYSVIEW_LOG, &ParamList); - va_end(ParamList); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_WarnfHost() -* -* Function description -* Print a warnin string which is formatted on the host by -* SystemViewer. -* -* Parameters -* s - String to be formatted. -* -* Additional information -* All format arguments are treated as 32-bit scalar values. -*/ -void SEGGER_SYSVIEW_WarnfHost(const char* s, ...) { - va_list ParamList; - - va_start(ParamList, s); - _VPrintHost(s, SEGGER_SYSVIEW_WARNING, &ParamList); - va_end(ParamList); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_ErrorfHost() -* -* Function description -* Print an error string which is formatted on the host by -* SystemViewer. -* -* Parameters -* s - String to be formatted. -* -* Additional information -* All format arguments are treated as 32-bit scalar values. -*/ -void SEGGER_SYSVIEW_ErrorfHost(const char* s, ...) { - va_list ParamList; - - va_start(ParamList, s); - _VPrintHost(s, SEGGER_SYSVIEW_ERROR, &ParamList); - va_end(ParamList); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_PrintfTargetEx() -* -* Function description -* Print a string which is formatted on the target before sent to -* the host with Additional information. -* -* Parameters -* s - String to be formatted. -* Options - Options for the string. i.e. Log level. -*/ -void SEGGER_SYSVIEW_PrintfTargetEx(const char* s, U32 Options, ...) { - va_list ParamList; - - va_start(ParamList, Options); - _VPrintTarget(s, Options, &ParamList); - va_end(ParamList); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_PrintfTarget() -* -* Function description -* Print a string which is formatted on the target before sent to -* the host. -* -* Parameters -* s - String to be formatted. -*/ -void SEGGER_SYSVIEW_PrintfTarget(const char* s, ...) { - va_list ParamList; - - va_start(ParamList, s); - _VPrintTarget(s, SEGGER_SYSVIEW_LOG, &ParamList); - va_end(ParamList); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_WarnfTarget() -* -* Function description -* Print a warning string which is formatted on the target before -* sent to the host. -* -* Parameters -* s - String to be formatted. -*/ -void SEGGER_SYSVIEW_WarnfTarget(const char* s, ...) { - va_list ParamList; - - va_start(ParamList, s); - _VPrintTarget(s, SEGGER_SYSVIEW_WARNING, &ParamList); - va_end(ParamList); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_ErrorfTarget() -* -* Function description -* Print an error string which is formatted on the target before -* sent to the host. -* -* Parameters -* s - String to be formatted. -*/ -void SEGGER_SYSVIEW_ErrorfTarget(const char* s, ...) { - va_list ParamList; - - va_start(ParamList, s); - _VPrintTarget(s, SEGGER_SYSVIEW_ERROR, &ParamList); - va_end(ParamList); -} -#endif // SEGGER_SYSVIEW_EXCLUDE_PRINTF - -/********************************************************************* -* -* SEGGER_SYSVIEW_Print() -* -* Function description -* Print a string to the host. -* -* Parameters -* s - String to sent. -*/ -void SEGGER_SYSVIEW_Print(const char* s) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); - ENCODE_U32(pPayload, SEGGER_SYSVIEW_LOG); - ENCODE_U32(pPayload, 0); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_Warn() -* -* Function description -* Print a warning string to the host. -* -* Parameters -* s - String to sent. -*/ -void SEGGER_SYSVIEW_Warn(const char* s) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); - ENCODE_U32(pPayload, SEGGER_SYSVIEW_WARNING); - ENCODE_U32(pPayload, 0); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_Error() -* -* Function description -* Print an error string to the host. -* -* Parameters -* s - String to sent. -*/ -void SEGGER_SYSVIEW_Error(const char* s) { - U8* pPayload; - U8* pPayloadStart; - RECORD_START(SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_STRING_LEN); - // - pPayload = _EncodeStr(pPayloadStart, s, SEGGER_SYSVIEW_MAX_STRING_LEN); - ENCODE_U32(pPayload, SEGGER_SYSVIEW_ERROR); - ENCODE_U32(pPayload, 0); - _SendPacket(pPayloadStart, pPayload, SYSVIEW_EVTID_PRINT_FORMATTED); - RECORD_END(); -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_EnableEvents() -* -* Function description -* Enable standard SystemView events to be generated. -* -* Parameters -* EnableMask - Events to be enabled. -*/ -void SEGGER_SYSVIEW_EnableEvents(U32 EnableMask) { - _SYSVIEW_Globals.DisabledEvents &= ~EnableMask; -} - -/********************************************************************* -* -* SEGGER_SYSVIEW_DisableEvents() -* -* Function description -* Disable standard SystemView events to not be generated. -* -* Parameters -* DisableMask - Events to be disabled. -*/ -void SEGGER_SYSVIEW_DisableEvents(U32 DisableMask) { - _SYSVIEW_Globals.DisabledEvents |= DisableMask; -} - - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW.h b/src/Segger_SystemView/SEGGER_SYSVIEW.h deleted file mode 100755 index a976152b..00000000 --- a/src/Segger_SystemView/SEGGER_SYSVIEW.h +++ /dev/null @@ -1,334 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** --------------------------- END-OF-HEADER ----------------------------- -File : SEGGER_SYSVIEW.h -Purpose : System visualization API. -Revision: $Rev: 3807 $ -*/ - -#ifndef SEGGER_SYSVIEW_H -#define SEGGER_SYSVIEW_H - -/********************************************************************* -* -* #include Section -* -********************************************************************** -*/ - -#include "SEGGER.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ - -#define SEGGER_SYSVIEW_VERSION 21000 - -#define SEGGER_SYSVIEW_INFO_SIZE 9 // Minimum size, which has to be reserved for a packet. 1-2 byte of message type, 0-2 byte of payload length, 1-5 bytes of timestamp. -#define SEGGER_SYSVIEW_QUANTA_U32 5 // Maximum number of bytes to encode a U32, should be reserved for each 32-bit value in a packet. - -#define SEGGER_SYSVIEW_LOG (0u) -#define SEGGER_SYSVIEW_WARNING (1u) -#define SEGGER_SYSVIEW_ERROR (2u) -#define SEGGER_SYSVIEW_FLAG_APPEND (1u << 6) - -#define SEGGER_SYSVIEW_PREPARE_PACKET(p) (p) + 4 -// -// SystemView events. First 32 IDs from 0 .. 31 are reserved for these -// -#define SYSVIEW_EVTID_NOP 0 // Dummy packet. -#define SYSVIEW_EVTID_OVERFLOW 1 -#define SYSVIEW_EVTID_ISR_ENTER 2 -#define SYSVIEW_EVTID_ISR_EXIT 3 -#define SYSVIEW_EVTID_TASK_START_EXEC 4 -#define SYSVIEW_EVTID_TASK_STOP_EXEC 5 -#define SYSVIEW_EVTID_TASK_START_READY 6 -#define SYSVIEW_EVTID_TASK_STOP_READY 7 -#define SYSVIEW_EVTID_TASK_CREATE 8 -#define SYSVIEW_EVTID_TASK_INFO 9 -#define SYSVIEW_EVTID_TRACE_START 10 -#define SYSVIEW_EVTID_TRACE_STOP 11 -#define SYSVIEW_EVTID_SYSTIME_CYCLES 12 -#define SYSVIEW_EVTID_SYSTIME_US 13 -#define SYSVIEW_EVTID_SYSDESC 14 -#define SYSVIEW_EVTID_USER_START 15 -#define SYSVIEW_EVTID_USER_STOP 16 -#define SYSVIEW_EVTID_IDLE 17 -#define SYSVIEW_EVTID_ISR_TO_SCHEDULER 18 -#define SYSVIEW_EVTID_TIMER_ENTER 19 -#define SYSVIEW_EVTID_TIMER_EXIT 20 -#define SYSVIEW_EVTID_STACK_INFO 21 -#define SYSVIEW_EVTID_MODULEDESC 22 - -#define SYSVIEW_EVTID_INIT 24 -#define SYSVIEW_EVTID_NAME_RESOURCE 25 -#define SYSVIEW_EVTID_PRINT_FORMATTED 26 -#define SYSVIEW_EVTID_NUMMODULES 27 -#define SYSVIEW_EVTID_END_CALL 28 -#define SYSVIEW_EVTID_TASK_TERMINATE 29 - -#define SYSVIEW_EVTID_EX 31 -// -// Event masks to disable/enable events -// -#define SYSVIEW_EVTMASK_NOP (1 << SYSVIEW_EVTID_NOP) -#define SYSVIEW_EVTMASK_OVERFLOW (1 << SYSVIEW_EVTID_OVERFLOW) -#define SYSVIEW_EVTMASK_ISR_ENTER (1 << SYSVIEW_EVTID_ISR_ENTER) -#define SYSVIEW_EVTMASK_ISR_EXIT (1 << SYSVIEW_EVTID_ISR_EXIT) -#define SYSVIEW_EVTMASK_TASK_START_EXEC (1 << SYSVIEW_EVTID_TASK_START_EXEC) -#define SYSVIEW_EVTMASK_TASK_STOP_EXEC (1 << SYSVIEW_EVTID_TASK_STOP_EXEC) -#define SYSVIEW_EVTMASK_TASK_START_READY (1 << SYSVIEW_EVTID_TASK_START_READY) -#define SYSVIEW_EVTMASK_TASK_STOP_READY (1 << SYSVIEW_EVTID_TASK_STOP_READY) -#define SYSVIEW_EVTMASK_TASK_CREATE (1 << SYSVIEW_EVTID_TASK_CREATE) -#define SYSVIEW_EVTMASK_TASK_INFO (1 << SYSVIEW_EVTID_TASK_INFO) -#define SYSVIEW_EVTMASK_TRACE_START (1 << SYSVIEW_EVTID_TRACE_START) -#define SYSVIEW_EVTMASK_TRACE_STOP (1 << SYSVIEW_EVTID_TRACE_STOP) -#define SYSVIEW_EVTMASK_SYSTIME_CYCLES (1 << SYSVIEW_EVTID_SYSTIME_CYCLES) -#define SYSVIEW_EVTMASK_SYSTIME_US (1 << SYSVIEW_EVTID_SYSTIME_US) -#define SYSVIEW_EVTMASK_SYSDESC (1 << SYSVIEW_EVTID_SYSDESC) -#define SYSVIEW_EVTMASK_USER_START (1 << SYSVIEW_EVTID_USER_START) -#define SYSVIEW_EVTMASK_USER_STOP (1 << SYSVIEW_EVTID_USER_STOP) -#define SYSVIEW_EVTMASK_IDLE (1 << SYSVIEW_EVTID_IDLE) -#define SYSVIEW_EVTMASK_ISR_TO_SCHEDULER (1 << SYSVIEW_EVTID_ISR_TO_SCHEDULER) -#define SYSVIEW_EVTMASK_TIMER_ENTER (1 << SYSVIEW_EVTID_TIMER_ENTER) -#define SYSVIEW_EVTMASK_TIMER_EXIT (1 << SYSVIEW_EVTID_TIMER_EXIT) -#define SYSVIEW_EVTMASK_STACK_INFO (1 << SYSVIEW_EVTID_STACK_INFO) -#define SYSVIEW_EVTMASK_MODULEDESC (1 << SYSVIEW_EVTID_MODULEDESC) - -#define SYSVIEW_EVTMASK_INIT (1 << SYSVIEW_EVTID_INIT) -#define SYSVIEW_EVTMASK_NAME_RESOURCE (1 << SYSVIEW_EVTID_NAME_RESOURCE) -#define SYSVIEW_EVTMASK_PRINT_FORMATTED (1 << SYSVIEW_EVTID_PRINT_FORMATTED) -#define SYSVIEW_EVTMASK_NUMMODULES (1 << SYSVIEW_EVTID_NUMMODULES) -#define SYSVIEW_EVTMASK_END_CALL (1 << SYSVIEW_EVTID_END_CALL) -#define SYSVIEW_EVTMASK_TASK_TERMINATE (1 << SYSVIEW_EVTID_TASK_TERMINATE) - -#define SYSVIEW_EVTMASK_EX (1 << SYSVIEW_EVTID_EX) - -#define SYSVIEW_EVTMASK_ALL_INTERRUPTS ( SYSVIEW_EVTMASK_ISR_ENTER \ - | SYSVIEW_EVTMASK_ISR_EXIT \ - | SYSVIEW_EVTMASK_ISR_TO_SCHEDULER) -#define SYSVIEW_EVTMASK_ALL_TASKS ( SYSVIEW_EVTMASK_TASK_START_EXEC \ - | SYSVIEW_EVTMASK_TASK_STOP_EXEC \ - | SYSVIEW_EVTMASK_TASK_START_READY \ - | SYSVIEW_EVTMASK_TASK_STOP_READY \ - | SYSVIEW_EVTMASK_TASK_CREATE \ - | SYSVIEW_EVTMASK_TASK_INFO \ - | SYSVIEW_EVTMASK_STACK_INFO \ - | SYSVIEW_EVTMASK_TASK_TERMINATE) - -/********************************************************************* -* -* Structures -* -********************************************************************** -*/ - -typedef struct { - U32 TaskID; - const char* sName; - U32 Prio; - U32 StackBase; - U32 StackSize; -} SEGGER_SYSVIEW_TASKINFO; - -typedef struct SEGGER_SYSVIEW_MODULE_STRUCT SEGGER_SYSVIEW_MODULE; - -struct SEGGER_SYSVIEW_MODULE_STRUCT { - const char* sModule; - U32 NumEvents; - U32 EventOffset; - void (*pfSendModuleDesc)(void); - SEGGER_SYSVIEW_MODULE* pNext; -}; - -typedef void (SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC)(void); - -/********************************************************************* -* -* API functions -* -********************************************************************** -*/ - -typedef struct { - U64 (*pfGetTime) (void); - void (*pfSendTaskList) (void); -} SEGGER_SYSVIEW_OS_API; - -/********************************************************************* -* -* Control and initialization functions -*/ -void SEGGER_SYSVIEW_Init (U32 SysFreq, U32 CPUFreq, const SEGGER_SYSVIEW_OS_API *pOSAPI, SEGGER_SYSVIEW_SEND_SYS_DESC_FUNC pfSendSysDesc); -void SEGGER_SYSVIEW_SetRAMBase (U32 RAMBaseAddress); -void SEGGER_SYSVIEW_Start (void); -void SEGGER_SYSVIEW_Stop (void); -void SEGGER_SYSVIEW_GetSysDesc (void); -void SEGGER_SYSVIEW_SendTaskList (void); -void SEGGER_SYSVIEW_SendTaskInfo (const SEGGER_SYSVIEW_TASKINFO* pInfo); -void SEGGER_SYSVIEW_SendSysDesc (const char* sSysDesc); - -/********************************************************************* -* -* Event recording functions -*/ -void SEGGER_SYSVIEW_RecordVoid (unsigned EventId); -void SEGGER_SYSVIEW_RecordU32 (unsigned EventId, U32 Para0); -void SEGGER_SYSVIEW_RecordU32x2 (unsigned EventId, U32 Para0, U32 Para1); -void SEGGER_SYSVIEW_RecordU32x3 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2); -void SEGGER_SYSVIEW_RecordU32x4 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3); -void SEGGER_SYSVIEW_RecordU32x5 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4); -void SEGGER_SYSVIEW_RecordU32x6 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5); -void SEGGER_SYSVIEW_RecordU32x7 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6); -void SEGGER_SYSVIEW_RecordU32x8 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7); -void SEGGER_SYSVIEW_RecordU32x9 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8); -void SEGGER_SYSVIEW_RecordU32x10 (unsigned EventId, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4, U32 Para5, U32 Para6, U32 Para7, U32 Para8, U32 Para9); -void SEGGER_SYSVIEW_RecordString (unsigned EventId, const char* pString); -void SEGGER_SYSVIEW_RecordSystime (void); -void SEGGER_SYSVIEW_RecordEnterISR (void); -void SEGGER_SYSVIEW_RecordExitISR (void); -void SEGGER_SYSVIEW_RecordExitISRToScheduler (void); -void SEGGER_SYSVIEW_RecordEnterTimer (U32 TimerId); -void SEGGER_SYSVIEW_RecordExitTimer (void); -void SEGGER_SYSVIEW_RecordEndCall (unsigned EventID); -void SEGGER_SYSVIEW_RecordEndCallU32 (unsigned EventID, U32 Para0); - -void SEGGER_SYSVIEW_OnIdle (void); -void SEGGER_SYSVIEW_OnTaskCreate (unsigned TaskId); -void SEGGER_SYSVIEW_OnTaskTerminate (unsigned TaskId); -void SEGGER_SYSVIEW_OnTaskStartExec (unsigned TaskId); -void SEGGER_SYSVIEW_OnTaskStopExec (void); -void SEGGER_SYSVIEW_OnTaskStartReady (unsigned TaskId); -void SEGGER_SYSVIEW_OnTaskStopReady (unsigned TaskId, unsigned Cause); -void SEGGER_SYSVIEW_OnUserStart (unsigned UserId); // Start of user defined event (such as a subroutine to profile) -void SEGGER_SYSVIEW_OnUserStop (unsigned UserId); // Start of user defined event - -void SEGGER_SYSVIEW_NameResource (U32 ResourceId, const char* sName); - -int SEGGER_SYSVIEW_SendPacket (U8* pPacket, U8* pPayloadEnd, unsigned EventId); - -/********************************************************************* -* -* Event parameter encoding functions -*/ -U8* SEGGER_SYSVIEW_EncodeU32 (U8* pPayload, unsigned Value); -U8* SEGGER_SYSVIEW_EncodeData (U8* pPayload, const char* pSrc, unsigned Len); -U8* SEGGER_SYSVIEW_EncodeString (U8* pPayload, const char* s, unsigned MaxLen); -U8* SEGGER_SYSVIEW_EncodeId (U8* pPayload, unsigned Id); -U32 SEGGER_SYSVIEW_ShrinkId (U32 Id); - - -/********************************************************************* -* -* Middleware module registration -*/ -void SEGGER_SYSVIEW_RegisterModule (SEGGER_SYSVIEW_MODULE* pModule); -void SEGGER_SYSVIEW_RecordModuleDescription (const SEGGER_SYSVIEW_MODULE* pModule, const char* sDescription); -void SEGGER_SYSVIEW_SendModule (U8 ModuleId); -void SEGGER_SYSVIEW_SendModuleDescription (void); -void SEGGER_SYSVIEW_SendNumModules (void); - -/********************************************************************* -* -* printf-Style functions -*/ -#ifndef SEGGER_SYSVIEW_EXCLUDE_PRINTF // Define in project to avoid warnings about variable parameter list -void SEGGER_SYSVIEW_PrintfHostEx (const char* s, U32 Options, ...); -void SEGGER_SYSVIEW_PrintfTargetEx (const char* s, U32 Options, ...); -void SEGGER_SYSVIEW_PrintfHost (const char* s, ...); -void SEGGER_SYSVIEW_PrintfTarget (const char* s, ...); -void SEGGER_SYSVIEW_WarnfHost (const char* s, ...); -void SEGGER_SYSVIEW_WarnfTarget (const char* s, ...); -void SEGGER_SYSVIEW_ErrorfHost (const char* s, ...); -void SEGGER_SYSVIEW_ErrorfTarget (const char* s, ...); -#endif - -void SEGGER_SYSVIEW_Print (const char* s); -void SEGGER_SYSVIEW_Warn (const char* s); -void SEGGER_SYSVIEW_Error (const char* s); - -/********************************************************************* -* -* Run-time configuration functions -*/ -void SEGGER_SYSVIEW_EnableEvents (U32 EnableMask); -void SEGGER_SYSVIEW_DisableEvents (U32 DisableMask); - -/********************************************************************* -* -* Application-provided functions -*/ -void SEGGER_SYSVIEW_Conf (void); -U32 SEGGER_SYSVIEW_X_GetTimestamp (void); -U32 SEGGER_SYSVIEW_X_GetInterruptId (void); - -#ifdef __cplusplus -} -#endif - -#endif - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h b/src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h deleted file mode 100755 index a8fa1948..00000000 --- a/src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h +++ /dev/null @@ -1,160 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** --------------------------- END-OF-HEADER ----------------------------- - -File : SEGGER_SYSVIEW_Conf.h -Purpose : SEGGER SystemView configuration. -Revision: $Rev: 3734 $ -*/ - -#ifndef SEGGER_SYSVIEW_CONF_H -#define SEGGER_SYSVIEW_CONF_H - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ -// -// Constants for known core configuration -// -#define SEGGER_SYSVIEW_CORE_OTHER 0 -#define SEGGER_SYSVIEW_CORE_CM0 1 // Cortex-M0/M0+/M1 -#define SEGGER_SYSVIEW_CORE_CM3 2 // Cortex-M3/M4/M7 -#define SEGGER_SYSVIEW_CORE_RX 3 // Renesas RX - -#if (defined __SES_ARM) || (defined __CROSSWORKS_ARM) || (defined __GNUC__) - #ifdef __ARM_ARCH_6M__ - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM0 - #elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)) - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM3 - #endif -#elif defined(__ICCARM__) - #if (defined (__ARM6M__) && (__CORE__ == __ARM6M__)) - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM0 - #elif ((defined (__ARM7M__) && (__CORE__ == __ARM7M__)) || (defined (__ARM7EM__) && (__CORE__ == __ARM7EM__))) - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM3 - #endif -#elif defined(__CC_ARM) - #if (defined(__TARGET_ARCH_6S_M)) - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM0 - #elif (defined(__TARGET_ARCH_7_M) || defined(__TARGET_ARCH_7E_M)) - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_CM3 - #endif -#elif defined(__ICCRX__) - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_RX -#elif defined(__RX) - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_RX -#endif - -#ifndef SEGGER_SYSVIEW_CORE - #define SEGGER_SYSVIEW_CORE SEGGER_SYSVIEW_CORE_OTHER -#endif - -/********************************************************************* -* -* Defines, configurable -* -********************************************************************** -*/ -/********************************************************************* -* -* SystemView buffer configuration -*/ -#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 // Number of bytes that SystemView uses for the buffer. -#define SEGGER_SYSVIEW_RTT_CHANNEL 1 // The RTT channel that SystemView will use. 0: Auto selection - -#define SEGGER_SYSVIEW_USE_STATIC_BUFFER 1 // Use a static buffer to generate events instead of a buffer on the stack - -#define SEGGER_SYSVIEW_POST_MORTEM_MODE 0 // 1: Enable post mortem analysis mode - -/********************************************************************* -* -* SystemView timestamp configuration -*/ -#if SEGGER_SYSVIEW_CORE == SEGGER_SYSVIEW_CORE_CM3 - #define SEGGER_SYSVIEW_GET_TIMESTAMP() (*(U32 *)(0xE0001004)) // Retrieve a system timestamp. Cortex-M cycle counter. - #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 // Define number of valid bits low-order delivered by clock source -#else - #define SEGGER_SYSVIEW_GET_TIMESTAMP() SEGGER_SYSVIEW_X_GetTimestamp() // Retrieve a system timestamp via user-defined function - #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 // Define number of valid bits low-order delivered by SEGGER_SYSVIEW_X_GetTimestamp() -#endif - -/********************************************************************* -* -* SystemView Id configuration -*/ -#define SEGGER_SYSVIEW_ID_BASE 0x10000000 // Default value for the lowest Id reported by the application. Can be overridden by the application via SEGGER_SYSVIEW_SetRAMBase(). (i.e. 0x20000000 when all Ids are an address in this RAM) -#define SEGGER_SYSVIEW_ID_SHIFT 2 // Number of bits to shift the Id to save bandwidth. (i.e. 2 when Ids are 4 byte aligned) - -/********************************************************************* -* -* SystemView interrupt configuration -*/ -#if SEGGER_SYSVIEW_CORE == SEGGER_SYSVIEW_CORE_CM3 - #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() ((*(U32 *)(0xE000ED04)) & 0x1FF) // Get the currently active interrupt Id. (i.e. read Cortex-M ICSR[8:0] = active vector) -#elif SEGGER_SYSVIEW_CORE == SEGGER_SYSVIEW_CORE_CM0 - #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() ((*(U32 *)(0xE000ED04)) & 0x3F) // Get the currently active interrupt Id. (i.e. read Cortex-M ICSR[5:0] = active vector) -#else - #define SEGGER_SYSVIEW_GET_INTERRUPT_ID() SEGGER_SYSVIEW_X_GetInterruptId() // Get the currently active interrupt Id from the user-provided function. -#endif - -#endif // SEGGER_SYSVIEW_CONF_H - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h b/src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h deleted file mode 100755 index 65719407..00000000 --- a/src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h +++ /dev/null @@ -1,179 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** --------------------------- END-OF-HEADER ----------------------------- -File : SEGGER_SYSVIEW_ConfDefaults.h -Purpose : Defines defaults for configurable defines used in - SEGGER SystemView. -Revision: $Rev: 3734 $ -*/ - -#ifndef SEGGER_SYSVIEW_CONFDEFAULTS_H -#define SEGGER_SYSVIEW_CONFDEFAULTS_H - -/********************************************************************* -* -* #include Section -* -********************************************************************** -*/ - -#include "SEGGER_SYSVIEW_Conf.h" -#include "SEGGER_RTT_Conf.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/********************************************************************* -* -* Configuration defaults -* -********************************************************************** -*/ - -// Number of bytes that SystemView uses for a buffer. -#ifndef SEGGER_SYSVIEW_RTT_BUFFER_SIZE -//#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 -#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 -#endif - -// The RTT channel that SystemView will use. -#ifndef SEGGER_SYSVIEW_RTT_CHANNEL - #define SEGGER_SYSVIEW_RTT_CHANNEL 0 -#endif -// Sanity check of RTT channel -#if (SEGGER_SYSVIEW_RTT_CHANNEL == 0) && (SEGGER_RTT_MAX_NUM_UP_BUFFERS < 2) - #error "SEGGER_RTT_MAX_NUM_UP_BUFFERS in SEGGER_RTT_Conf.h has to be > 1!" -#elif (SEGGER_SYSVIEW_RTT_CHANNEL >= SEGGER_RTT_MAX_NUM_UP_BUFFERS) - #error "SEGGER_RTT_MAX_NUM_UP_BUFFERS in SEGGER_RTT_Conf.h has to be > SEGGER_SYSVIEW_RTT_CHANNEL!" -#endif - -// Place the SystemView buffer into its own/the RTT section -#if !(defined SEGGER_SYSVIEW_BUFFER_SECTION) && (defined SEGGER_RTT_SECTION) - #define SEGGER_SYSVIEW_BUFFER_SECTION SEGGER_RTT_SECTION -#endif - -// Retrieve a system timestamp. This gets the Cortex-M cycle counter. -#ifndef SEGGER_SYSVIEW_GET_TIMESTAMP - #error "SEGGER_SYSVIEW_GET_TIMESTAMP has to be defined in SEGGER_SYSVIEW_Conf.h!" -#endif - -// Define number of valid bits low-order delivered by clock source. -#ifndef SEGGER_SYSVIEW_TIMESTAMP_BITS - #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 -#endif - -// Lowest Id reported by the Application. -#ifndef SEGGER_SYSVIEW_ID_BASE - #define SEGGER_SYSVIEW_ID_BASE 0 -#endif - -// Number of bits to shift Ids to save bandwidth -#ifndef SEGGER_SYSVIEW_ID_SHIFT - #define SEGGER_SYSVIEW_ID_SHIFT 0 -#endif - -#ifndef SEGGER_SYSVIEW_GET_INTERRUPT_ID - #error "SEGGER_SYSVIEW_GET_INTERRUPT_ID has to be defined in SEGGER_SYSVIEW_Conf.h!" -#endif - -#ifndef SEGGER_SYSVIEW_MAX_ARGUMENTS - #define SEGGER_SYSVIEW_MAX_ARGUMENTS 16 -#endif - -#ifndef SEGGER_SYSVIEW_MAX_STRING_LEN - #define SEGGER_SYSVIEW_MAX_STRING_LEN 128 -#endif - -// Use a static buffer instead of a buffer on the stack for packets -#ifndef SEGGER_SYSVIEW_USE_STATIC_BUFFER - #define SEGGER_SYSVIEW_USE_STATIC_BUFFER 1 -#endif - -// Maximum packet size used by SystemView for the static buffer -#ifndef SEGGER_SYSVIEW_MAX_PACKET_SIZE - #define SEGGER_SYSVIEW_MAX_PACKET_SIZE SEGGER_SYSVIEW_INFO_SIZE + SEGGER_SYSVIEW_MAX_STRING_LEN + 2 * SEGGER_SYSVIEW_QUANTA_U32 + SEGGER_SYSVIEW_MAX_ARGUMENTS * SEGGER_SYSVIEW_QUANTA_U32 -#endif - -// Use post-mortem analysis instead of real-time analysis -#ifndef SEGGER_SYSVIEW_POST_MORTEM_MODE - #define SEGGER_SYSVIEW_POST_MORTEM_MODE 0 -#endif - -// Configure how frequently syncronization is sent -#ifndef SEGGER_SYSVIEW_SYNC_PERIOD_SHIFT - #define SEGGER_SYSVIEW_SYNC_PERIOD_SHIFT 8 -#endif - -// Lock SystemView (nestable) -#ifndef SEGGER_SYSVIEW_LOCK - #define SEGGER_SYSVIEW_LOCK() SEGGER_RTT_LOCK() -#endif - -// Unlock SystemView (nestable) -#ifndef SEGGER_SYSVIEW_UNLOCK - #define SEGGER_SYSVIEW_UNLOCK() SEGGER_RTT_UNLOCK() -#endif - -#ifdef __cplusplus -} -#endif - -#endif - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c b/src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c deleted file mode 100755 index 611a1db9..00000000 --- a/src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c +++ /dev/null @@ -1,115 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** --------------------------- END-OF-HEADER ----------------------------- - -File : SEGGER_SYSVIEW_Config_NoOS.c -Purpose : Sample setup configuration of SystemView without an OS. -Revision: $Rev: 3734 $ -*/ -#include "SEGGER_SYSVIEW.h" - -// SystemcoreClock can be used in most CMSIS compatible projects. -// In non-CMSIS projects define SYSVIEW_CPU_FREQ. -extern unsigned int SystemCoreClock; - -/********************************************************************* -* -* Defines, configurable -* -********************************************************************** -*/ -// The application name to be displayed in SystemViewer -#define SYSVIEW_APP_NAME "Smoothie2" - -// The target device name -#define SYSVIEW_DEVICE_NAME "Cortex-M4" - -// Frequency of the timestamp. Must match SEGGER_SYSVIEW_Conf.h -#define SYSVIEW_TIMESTAMP_FREQ (SystemCoreClock) - -// System Frequency. SystemcoreClock is used in most CMSIS compatible projects. -#define SYSVIEW_CPU_FREQ (SystemCoreClock) - -// The lowest RAM address used for IDs (pointers) -#define SYSVIEW_RAM_BASE (0x10000000) - -/********************************************************************* -* -* _cbSendSystemDesc() -* -* Function description -* Sends SystemView description strings. -*/ -static void _cbSendSystemDesc(void) { - SEGGER_SYSVIEW_SendSysDesc("N="SYSVIEW_APP_NAME",D="SYSVIEW_DEVICE_NAME); - SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick,I#27=acceleration_tick(),I#28=step_tick(),I#29=unstep_tick(),I#30=Timer2,I#100=Endstops::Endstops()"); -} - -/********************************************************************* -* -* Global functions -* -********************************************************************** -*/ -void SEGGER_SYSVIEW_Conf(void) { - SEGGER_SYSVIEW_Init(SYSVIEW_TIMESTAMP_FREQ, SYSVIEW_CPU_FREQ, - 0, _cbSendSystemDesc); - SEGGER_SYSVIEW_SetRAMBase(SYSVIEW_RAM_BASE); -} - -/*************************** End of file ****************************/ diff --git a/src/Segger_SystemView/SEGGER_SYSVIEW_Int.h b/src/Segger_SystemView/SEGGER_SYSVIEW_Int.h deleted file mode 100755 index c04f339d..00000000 --- a/src/Segger_SystemView/SEGGER_SYSVIEW_Int.h +++ /dev/null @@ -1,110 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 2015 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER SystemView * Real-time application analysis * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* SEGGER strongly recommends to not make any changes * -* to or modify the source code of this software in order to stay * -* compatible with the RTT protocol and J-Link. * -* * -* Redistribution and use in source and binary forms, with or * -* without modification, are permitted provided that the following * -* conditions are met: * -* * -* o Redistributions of source code must retain the above copyright * -* notice, this list of conditions and the following disclaimer. * -* * -* o Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the following * -* disclaimer in the documentation and/or other materials provided * -* with the distribution. * -* * -* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * -* nor the names of its contributors may be used to endorse or * -* promote products derived from this software without specific * -* prior written permission. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* SystemView version: V2.40a * -* * -********************************************************************** --------------------------- END-OF-HEADER ----------------------------- -File : SEGGER_SYSVIEW_Int.h -Purpose : SEGGER SystemView internal header. -Revision: $Rev: 3734 $ -*/ - -#ifndef SEGGER_SYSVIEW_INT_H -#define SEGGER_SYSVIEW_INT_H - -/********************************************************************* -* -* #include Section -* -********************************************************************** -*/ - -#include "SEGGER_SYSVIEW.h" -#include "SEGGER_SYSVIEW_Conf.h" -#include "SEGGER_SYSVIEW_ConfDefaults.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/********************************************************************* -* -* Private data types -* -********************************************************************** -*/ -// -// Commands that Host can send to target -// -typedef enum { - SEGGER_SYSVIEW_COMMAND_ID_START = 1, - SEGGER_SYSVIEW_COMMAND_ID_STOP, - SEGGER_SYSVIEW_COMMAND_ID_GET_SYSTIME, - SEGGER_SYSVIEW_COMMAND_ID_GET_TASKLIST, - SEGGER_SYSVIEW_COMMAND_ID_GET_SYSDESC, - SEGGER_SYSVIEW_COMMAND_ID_GET_NUMMODULES, - SEGGER_SYSVIEW_COMMAND_ID_GET_MODULEDESC, - // Extended commands: Commands >= 128 have a second parameter - SEGGER_SYSVIEW_COMMAND_ID_GET_MODULE = 128, -} SEGGER_SYSVIEW_COMMAND_ID; - -#ifdef __cplusplus -} -#endif - -#endif - -/*************************** End of file ****************************/ From 95e974ed2bc4e0aab38d6375ed09e337e1e5522f Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 23 Oct 2016 10:57:41 +1300 Subject: [PATCH 07/66] modified: src/Segger_SystemView/README.md --- src/Segger_SystemView/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md index 8c2d9ef6..08efdcf2 100644 --- a/src/Segger_SystemView/README.md +++ b/src/Segger_SystemView/README.md @@ -3,13 +3,23 @@ Go to http://www.segger.com and download their Systemview free tool and place the source code in this folder, you should end up with the following files: src/Segger_SystemView/SEGGER_SYSVIEW_Int.h + src/Segger_SystemView/Global.h + src/Segger_SystemView/SEGGER.h + src/Segger_SystemView/SEGGER_RTT.c + src/Segger_SystemView/SEGGER_RTT.h + src/Segger_SystemView/SEGGER_RTT_Conf.h + src/Segger_SystemView/SEGGER_SYSVIEW.c + src/Segger_SystemView/SEGGER_SYSVIEW.h + src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h + src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h + src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c From 0e1de270ea49c3afb7ac7e83500f0642ed44d246 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 23 Oct 2016 15:46:06 +1300 Subject: [PATCH 08/66] modified: src/Segger_SystemView/README.md --- src/Segger_SystemView/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md index 08efdcf2..7a145b22 100644 --- a/src/Segger_SystemView/README.md +++ b/src/Segger_SystemView/README.md @@ -23,3 +23,31 @@ src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c + + +You will need to make the following changes in src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c + +old +#define SYSVIEW_APP_NAME "Demo Application" +new +#define SYSVIEW_APP_NAME "Smoothie2" + +old +SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick"); +new +SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick,I#27=acceleration_tick(),I#28=step_tick(),I#29=unstep_tick(),I#30=Timer2,I#100=Endstops::Endstops()"); + +If you find that you are running out of buffering between the CPU and your PC, then change src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h as follows + +old +#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 +new +#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 + +Note: 4096 is the largest buffer size supported; you can also increase the speed of the JTAG interface to 15MHz when you start your JLinkGDBServer: + +JLinkGDBServer -if jtag -device LPC4330 -speed 15000 + +Note: I run my JLinkGDBServer on a separate Ubuntu machine onto which my Firepick is attached. + + From c2f1e0c148971ae64df3fcd39492c74b799d4735 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 23 Oct 2016 15:51:08 +1300 Subject: [PATCH 09/66] modified: src/Segger_SystemView/README.md --- src/Segger_SystemView/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md index 7a145b22..7668ff47 100644 --- a/src/Segger_SystemView/README.md +++ b/src/Segger_SystemView/README.md @@ -28,9 +28,9 @@ src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c You will need to make the following changes in src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c old -#define SYSVIEW_APP_NAME "Demo Application" +\#define SYSVIEW_APP_NAME "Demo Application" new -#define SYSVIEW_APP_NAME "Smoothie2" +\#define SYSVIEW_APP_NAME "Smoothie2" old SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick"); @@ -40,9 +40,9 @@ SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick,I#27=acceleration_tick(),I#28=step_tick If you find that you are running out of buffering between the CPU and your PC, then change src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h as follows old -#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 +\#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 new -#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 +\#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 Note: 4096 is the largest buffer size supported; you can also increase the speed of the JTAG interface to 15MHz when you start your JLinkGDBServer: From 413dad3b489ed093cfc89ae5ee2beb584033382b Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 23 Oct 2016 15:57:32 +1300 Subject: [PATCH 10/66] modified: src/Segger_SystemView/README.md --- src/Segger_SystemView/README.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md index 7668ff47..f8e31ed1 100644 --- a/src/Segger_SystemView/README.md +++ b/src/Segger_SystemView/README.md @@ -1,53 +1,52 @@ #Segger JTrace SystemView debugging and integration Go to http://www.segger.com and download their Systemview free tool and place the source code in this folder, you should end up with the following files: - +``` src/Segger_SystemView/SEGGER_SYSVIEW_Int.h - src/Segger_SystemView/Global.h - src/Segger_SystemView/SEGGER.h - src/Segger_SystemView/SEGGER_RTT.c - src/Segger_SystemView/SEGGER_RTT.h - src/Segger_SystemView/SEGGER_RTT_Conf.h - src/Segger_SystemView/SEGGER_SYSVIEW.c - src/Segger_SystemView/SEGGER_SYSVIEW.h - src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h - src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h - src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c - +``` You will need to make the following changes in src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c old +``` \#define SYSVIEW_APP_NAME "Demo Application" +``` new +``` \#define SYSVIEW_APP_NAME "Smoothie2" - +``` old +``` SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick"); +``` new +``` SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick,I#27=acceleration_tick(),I#28=step_tick(),I#29=unstep_tick(),I#30=Timer2,I#100=Endstops::Endstops()"); - +``` If you find that you are running out of buffering between the CPU and your PC, then change src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h as follows old +``` \#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 +``` new +``` \#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 - +``` Note: 4096 is the largest buffer size supported; you can also increase the speed of the JTAG interface to 15MHz when you start your JLinkGDBServer: JLinkGDBServer -if jtag -device LPC4330 -speed 15000 -Note: I run my JLinkGDBServer on a separate Ubuntu machine onto which my Firepick is attached. +Note: I run my JLinkGDBServer on a separate Ubuntu machine onto which my Firepick is attached and use TCP/IP to connect to it. From 2d2106f396037f1a6e160ef7b1e68d4a5fe71af2 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 23 Oct 2016 16:03:03 +1300 Subject: [PATCH 11/66] modified: src/Segger_SystemView/README.md --- src/Segger_SystemView/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md index f8e31ed1..f9aaf56b 100644 --- a/src/Segger_SystemView/README.md +++ b/src/Segger_SystemView/README.md @@ -1,6 +1,6 @@ #Segger JTrace SystemView debugging and integration -Go to http://www.segger.com and download their Systemview free tool and place the source code in this folder, you should end up with the following files: +Go to http://www.segger.com and download their SystemView free tool and copy only some of the source code files so you end up with the following files: ``` src/Segger_SystemView/SEGGER_SYSVIEW_Int.h src/Segger_SystemView/Global.h @@ -19,17 +19,17 @@ You will need to make the following changes in src/Segger_SystemView/SEGGER_SYSV old ``` -\#define SYSVIEW_APP_NAME "Demo Application" +#define SYSVIEW_APP_NAME "Demo Application" ``` new ``` -\#define SYSVIEW_APP_NAME "Smoothie2" +#define SYSVIEW_APP_NAME "Smoothie2" ``` old ``` SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick"); ``` -new +new (note the line is long and scrolls off the right hand side of the page, make sure you copy it all) ``` SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick,I#27=acceleration_tick(),I#28=step_tick(),I#29=unstep_tick(),I#30=Timer2,I#100=Endstops::Endstops()"); ``` @@ -37,11 +37,11 @@ If you find that you are running out of buffering between the CPU and your PC, t old ``` -\#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 +#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 1024 ``` new ``` -\#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 +#define SEGGER_SYSVIEW_RTT_BUFFER_SIZE 4096 ``` Note: 4096 is the largest buffer size supported; you can also increase the speed of the JTAG interface to 15MHz when you start your JLinkGDBServer: From 544f988f5182090b16218340aac0c055ae8bb391 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 09:32:39 +1300 Subject: [PATCH 12/66] modified: libs/ADC/BurstADC.cpp modified: libs/HeapRing.cpp modified: libs/HeapRing.h modified: libs/Kernel.cpp modified: libs/Kernel.h modified: libs/StepTicker.cpp modified: libs/StepTicker.h modified: libs/StepperMotor.cpp modified: libs/StepperMotor.h modified: modules/communication/GcodeDispatch.cpp modified: modules/communication/utils/Gcode.cpp modified: modules/communication/utils/Gcode.h modified: modules/robot/Block.cpp modified: modules/robot/Block.h modified: modules/robot/Conveyor.cpp modified: modules/robot/Conveyor.h modified: modules/robot/Planner.cpp modified: modules/robot/Planner.h modified: modules/robot/Robot.cpp modified: modules/robot/Robot.h deleted: modules/robot/Stepper.cpp deleted: modules/robot/Stepper.h modified: modules/tools/drillingcycles/Drillingcycles.cpp modified: modules/tools/endstops/Endstops.cpp modified: modules/tools/endstops/Endstops.h modified: modules/tools/filamentdetector/FilamentDetector.cpp modified: modules/tools/laser/Laser.cpp modified: modules/tools/laser/Laser.h modified: modules/tools/spindle/Spindle.cpp modified: modules/tools/switch/Switch.cpp modified: modules/tools/temperaturecontrol/TemperatureControl.cpp modified: modules/tools/temperaturecontrol/TemperatureControl.h modified: modules/tools/temperaturecontrol/TemperatureControlPool.cpp modified: modules/tools/temperaturecontrol/TemperatureControlPool.h modified: modules/tools/temperaturecontrol/max31855.cpp modified: modules/tools/temperaturecontrol/max31855.h modified: modules/tools/temperaturecontrol/predefined_thermistors.h modified: modules/tools/toolmanager/Tool.h modified: modules/tools/toolmanager/ToolManager.cpp modified: modules/utils/PlayLed/PlayLed.cpp modified: modules/utils/currentcontrol/ad5206.h modified: modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp modified: modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp --- src/libs/TSRingBuffer.h | 64 +++++++++++++ .../tools/temperaturecontrol/AD8495.cpp | 90 +++++++++++++++++++ src/modules/tools/temperaturecontrol/AD8495.h | 42 +++++++++ 3 files changed, 196 insertions(+) create mode 100644 src/libs/TSRingBuffer.h create mode 100644 src/modules/tools/temperaturecontrol/AD8495.cpp create mode 100644 src/modules/tools/temperaturecontrol/AD8495.h diff --git a/src/libs/TSRingBuffer.h b/src/libs/TSRingBuffer.h new file mode 100644 index 00000000..adbea5e9 --- /dev/null +++ b/src/libs/TSRingBuffer.h @@ -0,0 +1,64 @@ +// +// Simple fixed size ring buffer. +// Manage objects by value. +// Thread safe for single Producer and single Consumer. +// By Dennis Lang http://home.comcast.net/~lang.dennis/code/ring/ring.html +// Slightly modified for naming + +#pragma once + +template +class TSRingBuffer +{ +public: + TSRingBuffer() + : m_size(RingSize), m_buffer(new T[RingSize]), m_rIndex(0), m_wIndex(0) + { } + + ~TSRingBuffer() + { + delete [] m_buffer; + }; + + size_t next(size_t n) const + { + return (n + 1) % m_size; + } + + bool empty() const + { + return (m_rIndex == m_wIndex); + } + + bool full() const + { + return (next(m_wIndex) == m_rIndex); + } + + bool put(const T &value) + { + if (full()) + return false; + m_buffer[m_wIndex] = value; + m_wIndex = next(m_wIndex); + return true; + } + + bool get(T &value) + { + if (empty()) + return false; + value = m_buffer[m_rIndex]; + m_rIndex = next(m_rIndex); + return true; + } + +private: + size_t m_size; + T *m_buffer; + + // volatile is only used to keep compiler from placing values in registers. + // volatile does NOT make the index thread safe. + volatile size_t m_rIndex; + volatile size_t m_wIndex; +}; diff --git a/src/modules/tools/temperaturecontrol/AD8495.cpp b/src/modules/tools/temperaturecontrol/AD8495.cpp new file mode 100644 index 00000000..8b9a7057 --- /dev/null +++ b/src/modules/tools/temperaturecontrol/AD8495.cpp @@ -0,0 +1,90 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#include "AD8495.h" +#include "libs/Kernel.h" +#include "libs/Pin.h" +#include "Config.h" +#include "checksumm.h" +#include "Adc.h" +#include "ConfigValue.h" +#include "libs/Median.h" +#include "utils.h" +#include "StreamOutputPool.h" + +#include + +//TODO ADDBACK #include "MRI_Hooks.h" + +#define UNDEFINED -1 + +#define AD8495_pin_checksum CHECKSUM("ad8495_pin") +#define AD8495_offset_checksum CHECKSUM("ad8495_offset") + +AD8495::AD8495() +{ + min_temp= 999; + max_temp= 0; +} + +AD8495::~AD8495() +{ +} + +// Get configuration from the config file +void AD8495::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) +{ + // Thermistor pin for ADC readings + this->AD8495_pin.from_string(THEKERNEL->config->value(module_checksum, name_checksum, AD8495_pin_checksum)->required()->as_string()); + this->AD8495_offset = THEKERNEL->config->value(module_checksum, name_checksum, AD8495_offset_checksum)->by_default(0)->as_number(); // Stated offset. For Adafruit board it is 250C. If pin 2(REF) of amplifier is connected to 0V then there is 0C offset. + + THEKERNEL->adc->enable_pin(&AD8495_pin); +} + + +float AD8495::get_temperature() +{ + float t= adc_value_to_temperature(new_AD8495_reading()); + // keep track of min/max for M305 + if(t > max_temp) max_temp= t; + if(t < min_temp) min_temp= t; + return t; +} + +void AD8495::get_raw() +{ + + int adc_value= new_AD8495_reading(); + const uint32_t max_adc_value= THEKERNEL->adc->get_max_value(); + float t=((float)adc_value)/(((float)max_adc_value)/3.3*0.005); + + t = t - this->AD8495_offset; + + THEKERNEL->streams->printf("adc= %d, max_adc= %lu, temp= %f, offset = %f\n", adc_value,max_adc_value,t, this->AD8495_offset); + + // reset the min/max + min_temp= max_temp= t; +} + +float AD8495::adc_value_to_temperature(uint32_t adc_value) +{ + const uint32_t max_adc_value= THEKERNEL->adc->get_max_value(); + if ((adc_value >= max_adc_value)) + return infinityf(); + + float t=((float)adc_value)/(((float)max_adc_value)/3.3*0.005); + + t=t-this->AD8495_offset; + + return t; +} + +int AD8495::new_AD8495_reading() +{ + // filtering now done in ADC + return THEKERNEL->adc->read(&AD8495_pin); +} diff --git a/src/modules/tools/temperaturecontrol/AD8495.h b/src/modules/tools/temperaturecontrol/AD8495.h new file mode 100644 index 00000000..cea83c76 --- /dev/null +++ b/src/modules/tools/temperaturecontrol/AD8495.h @@ -0,0 +1,42 @@ +/* + this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl). + smoothie 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. + smoothie 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 smoothie. if not, see . +*/ + +#ifndef AD8495_H +#define AD8495_H + +#include "TempSensor.h" +#include "RingBuffer.h" +#include "Pin.h" + +#include + +#define QUEUE_LEN 32 + +class StreamOutput; + +class AD8495 : public TempSensor +{ + public: + AD8495(); + ~AD8495(); + + // TempSensor interface. + void UpdateConfig(uint16_t module_checksum, uint16_t name_checksum); + float get_temperature(); + void get_raw(); + + private: + int new_AD8495_reading(); + float adc_value_to_temperature(uint32_t adc_value); + + Pin AD8495_pin; + float AD8495_offset; + + float min_temp, max_temp; +}; + +#endif From 1814b1074294314f68d504f154beaca8d1847bd6 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 09:42:24 +1300 Subject: [PATCH 13/66] modified: libs/ADC/BurstADC.cpp modified: libs/HeapRing.cpp modified: libs/HeapRing.h modified: libs/Kernel.cpp modified: libs/Kernel.h modified: libs/StepTicker.cpp modified: libs/StepTicker.h modified: libs/StepperMotor.cpp modified: libs/StepperMotor.h modified: modules/communication/GcodeDispatch.cpp modified: modules/communication/utils/Gcode.cpp modified: modules/communication/utils/Gcode.h modified: modules/robot/Block.cpp modified: modules/robot/Block.h modified: modules/robot/Conveyor.cpp modified: modules/robot/Conveyor.h modified: modules/robot/Planner.cpp modified: modules/robot/Planner.h modified: modules/robot/Robot.cpp modified: modules/robot/Robot.h deleted: modules/robot/Stepper.cpp deleted: modules/robot/Stepper.h modified: modules/tools/drillingcycles/Drillingcycles.cpp modified: modules/tools/endstops/Endstops.cpp modified: modules/tools/endstops/Endstops.h modified: modules/tools/filamentdetector/FilamentDetector.cpp modified: modules/tools/laser/Laser.cpp modified: modules/tools/laser/Laser.h modified: modules/tools/spindle/Spindle.cpp modified: modules/tools/switch/Switch.cpp modified: modules/tools/temperaturecontrol/TemperatureControl.cpp modified: modules/tools/temperaturecontrol/TemperatureControl.h modified: modules/tools/temperaturecontrol/TemperatureControlPool.h modified: modules/tools/temperaturecontrol/max31855.cpp modified: modules/tools/temperaturecontrol/max31855.h modified: modules/tools/temperaturecontrol/predefined_thermistors.h modified: modules/tools/toolmanager/Tool.h modified: modules/tools/toolmanager/ToolManager.cpp modified: modules/utils/PlayLed/PlayLed.cpp modified: modules/utils/currentcontrol/ad5206.h modified: modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp modified: modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp --- src/libs/ADC/BurstADC.cpp | 1 + src/libs/HeapRing.cpp | 19 +- src/libs/HeapRing.h | 9 +- src/libs/Kernel.cpp | 33 +- src/libs/Kernel.h | 5 +- src/libs/StepTicker.cpp | 342 +++++---- src/libs/StepTicker.h | 71 +- src/libs/StepperMotor.cpp | 216 +----- src/libs/StepperMotor.h | 94 +-- src/modules/communication/GcodeDispatch.cpp | 13 +- src/modules/communication/utils/Gcode.cpp | 6 - src/modules/communication/utils/Gcode.h | 1 - src/modules/robot/Block.cpp | 300 +++++--- src/modules/robot/Block.h | 71 +- src/modules/robot/Conveyor.cpp | 262 ++++--- src/modules/robot/Conveyor.h | 36 +- src/modules/robot/Planner.cpp | 111 +-- src/modules/robot/Planner.h | 10 +- src/modules/robot/Robot.cpp | 718 ++++++++++++------ src/modules/robot/Robot.h | 62 +- src/modules/robot/Stepper.cpp | 287 ------- src/modules/robot/Stepper.h | 54 -- .../tools/drillingcycles/Drillingcycles.cpp | 15 +- src/modules/tools/endstops/Endstops.cpp | 553 ++++++-------- src/modules/tools/endstops/Endstops.h | 40 +- .../filamentdetector/FilamentDetector.cpp | 5 +- src/modules/tools/laser/Laser.cpp | 232 ++++-- src/modules/tools/laser/Laser.h | 36 +- src/modules/tools/spindle/Spindle.cpp | 7 +- src/modules/tools/switch/Switch.cpp | 27 +- .../temperaturecontrol/TemperatureControl.cpp | 56 +- .../temperaturecontrol/TemperatureControl.h | 10 + .../TemperatureControlPool.h | 4 - .../tools/temperaturecontrol/max31855.cpp | 10 +- .../tools/temperaturecontrol/max31855.h | 2 +- .../predefined_thermistors.h | 2 + src/modules/tools/toolmanager/Tool.h | 11 +- src/modules/tools/toolmanager/ToolManager.cpp | 14 +- src/modules/utils/PlayLed/PlayLed.cpp | 2 +- src/modules/utils/currentcontrol/ad5206.h | 5 +- .../drivers/TMC21X/TMC21X.cpp | 2 +- .../drivers/TMC26X/TMC26X.cpp | 8 +- 42 files changed, 1870 insertions(+), 1892 deletions(-) delete mode 100644 src/modules/robot/Stepper.cpp delete mode 100644 src/modules/robot/Stepper.h diff --git a/src/libs/ADC/BurstADC.cpp b/src/libs/ADC/BurstADC.cpp index b5aa7cdd..c1d7f8b9 100644 --- a/src/libs/ADC/BurstADC.cpp +++ b/src/libs/ADC/BurstADC.cpp @@ -160,6 +160,7 @@ int BurstADC::_pin_to_channel(PinName pin) { PinName BurstADC::channel_to_pin(int chan) { //LPC43xx has 2 ADC with 8 pins each + 8 adc only pins. Current structure of this function is not suitable + //TODO We need to fix this routine PinName a; return a; } diff --git a/src/libs/HeapRing.cpp b/src/libs/HeapRing.cpp index b4d3c4ac..a75411c8 100644 --- a/src/libs/HeapRing.cpp +++ b/src/libs/HeapRing.cpp @@ -11,12 +11,14 @@ template HeapRing::HeapRing() { head_i = tail_i = length = 0; + isr_tail_i = tail_i; ring = NULL; } template HeapRing::HeapRing(unsigned int length) { head_i = tail_i = 0; + isr_tail_i = tail_i; ring = new kind[length]; // TODO: handle allocation failure this->length = length; @@ -29,6 +31,7 @@ template HeapRing::HeapRing(unsigned int length) template HeapRing::~HeapRing() { head_i = tail_i = length = 0; + isr_tail_i = tail_i; if (ring) delete [] ring; ring = NULL; @@ -38,7 +41,7 @@ template HeapRing::~HeapRing() * index accessors (protected) */ -template unsigned int HeapRing::next(unsigned int item) +template unsigned int HeapRing::next(unsigned int item) const { if (length == 0) return 0; @@ -49,7 +52,7 @@ template unsigned int HeapRing::next(unsigned int item) return item; } -template unsigned int HeapRing::prev(unsigned int item) +template unsigned int HeapRing::prev(unsigned int item) const { if (length == 0) return 0; @@ -127,20 +130,20 @@ template void HeapRing::consume_tail() * queue status accessors */ -template bool HeapRing::is_full() +template bool HeapRing::is_full() const { - __disable_irq(); + //__disable_irq(); bool r = (next(head_i) == tail_i); - __enable_irq(); + //__enable_irq(); return r; } -template bool HeapRing::is_empty() +template bool HeapRing::is_empty() const { - __disable_irq(); + //__disable_irq(); bool r = (head_i == tail_i); - __enable_irq(); + //__enable_irq(); return r; } diff --git a/src/libs/HeapRing.h b/src/libs/HeapRing.h index c0aef58a..ed28f9cc 100644 --- a/src/libs/HeapRing.h +++ b/src/libs/HeapRing.h @@ -35,8 +35,8 @@ template class HeapRing { /* * queue status */ - bool is_empty(void); - bool is_full(void); + bool is_empty(void) const; + bool is_full(void) const; /* * resize @@ -64,8 +64,8 @@ template class HeapRing { kind& item(unsigned int); kind* item_ref(unsigned int); - unsigned int next(unsigned int); - unsigned int prev(unsigned int); + unsigned int next(unsigned int) const; + unsigned int prev(unsigned int) const; /* * buffer variables @@ -74,6 +74,7 @@ template class HeapRing { volatile unsigned int head_i; volatile unsigned int tail_i; + volatile unsigned int isr_tail_i; private: kind* ring; diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 2039bdd0..38a7ef7d 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -22,7 +22,6 @@ #include "modules/communication/GcodeDispatch.h" #include "modules/robot/Planner.h" #include "modules/robot/Robot.h" -#include "modules/robot/Stepper.h" #include "modules/robot/Conveyor.h" #include "StepperMotor.h" #include "BaseSolution.h" @@ -41,7 +40,6 @@ #define base_stepping_frequency_checksum CHECKSUM("base_stepping_frequency") #define microseconds_per_step_pulse_checksum CHECKSUM("microseconds_per_step_pulse") -#define acceleration_ticks_per_second_checksum CHECKSUM("acceleration_ticks_per_second") #define disable_leds_checksum CHECKSUM("leds_disable") #define grbl_mode_checksum CHECKSUM("grbl_mode") #define ok_per_line_checksum CHECKSUM("ok_per_line") @@ -84,23 +82,29 @@ Kernel::Kernel(){ //some boards don't have leds.. TOO BAD! this->use_leds= !this->config->value( disable_leds_checksum )->by_default(false)->as_bool(); + + #ifdef CNC + this->grbl_mode= this->config->value( grbl_mode_checksum )->by_default(true)->as_bool(); + #else this->grbl_mode= this->config->value( grbl_mode_checksum )->by_default(false)->as_bool(); + #endif + + // we exepct ok per line now not per G code, setting this to false will return to the old (incorrect) way of ok per G code this->ok_per_line= this->config->value( ok_per_line_checksum )->by_default(true)->as_bool(); // Configure the step ticker this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); - float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); - this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(1000)->as_number(); +// float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); + //this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(1000)->as_number(); this->step_ticker = new StepTicker(); // Core modules + this->add_module( this->conveyor = new Conveyor() ); this->add_module( this->gcode_dispatch = new GcodeDispatch() ); this->add_module( this->robot = new Robot() ); - this->add_module( this->stepper = new Stepper() ); - this->add_module( this->conveyor = new Conveyor() ); - // TOADDBACK this->add_module( this->simpleshell = new SimpleShell() ); + //this->add_module( this->simpleshell = new SimpleShell() ); this->planner = new Planner(); @@ -123,7 +127,7 @@ std::string Kernel::get_query_string() str.append("Home,"); }else if(feed_hold) { str.append("Hold,"); - }else if(this->conveyor->is_queue_empty()) { + }else if(this->conveyor->is_idle()) { str.append("Idle,"); }else{ running= true; @@ -186,7 +190,7 @@ void Kernel::call_event(_EVENT_ENUM id_event, void * argument){ bool was_idle= true; if(id_event == ON_HALT) { this->halted= (argument == nullptr); - was_idle= conveyor->is_queue_empty(); // see if we were doing anything like printing + was_idle= conveyor->is_idle(); // see if we were doing anything like printing } // send to all registered modules @@ -208,3 +212,14 @@ bool Kernel::kernel_has_event(_EVENT_ENUM id_event, Module *mod) } return false; } + +void Kernel::unregister_for_event(_EVENT_ENUM id_event, Module *mod) +{ + for (auto i = hooks[id_event].begin(); i != hooks[id_event].end(); ++i) { + if(*i == mod) { + hooks[id_event].erase(i); + return; + } + } +} + diff --git a/src/libs/Kernel.h b/src/libs/Kernel.h index 47edc2b1..023c21d3 100644 --- a/src/libs/Kernel.h +++ b/src/libs/Kernel.h @@ -9,6 +9,8 @@ #define KERNEL_H #define THEKERNEL Kernel::instance +#define THECONVEYOR THEKERNEL->conveyor +#define THEROBOT THEKERNEL->robot #include "Module.h" #include @@ -24,7 +26,6 @@ class SerialConsole; class StreamOutputPool; class GcodeDispatch; class Robot; -class Stepper; class Planner; class StepTicker; class Adc; @@ -61,7 +62,6 @@ class Kernel { StreamOutputPool* streams; GcodeDispatch* gcode_dispatch; Robot* robot; - Stepper* stepper; Planner* planner; Config* config; Conveyor* conveyor; @@ -74,7 +74,6 @@ class Kernel { Adc* adc; std::string current_path; uint32_t base_stepping_frequency; - uint32_t acceleration_ticks_per_second; private: // When a module asks to be called for a specific event ( a hook ), this is where that request is remembered diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 9d3653c5..23987cfb 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -13,6 +13,9 @@ #include "libs/Kernel.h" #include "StepperMotor.h" #include "StreamOutputPool.h" +#include "Block.h" +#include "Conveyor.h" + //#include "system_LPC17xx.h" // mbed.h lib #include #include @@ -20,19 +23,19 @@ #include "SEGGER_RTT_Conf.h" #ifdef STEPTICKER_DEBUG_PIN +// debug pins, only used if defined in src/makefile #include "gpio.h" -extern GPIO stepticker_debug_pin; +GPIO stepticker_debug_pin(STEPTICKER_DEBUG_PIN); +#define SET_STEPTICKER_DEBUG_PIN(n) {if(n) stepticker_debug_pin.set(); else stepticker_debug_pin.clear(); } +#else +#define SET_STEPTICKER_DEBUG_PIN(n) #endif +StepTicker *StepTicker::instance; -// StepTicker handles the base frequency ticking for the Stepper Motors / Actuators -// It has a list of those, and calls their tick() functions at regular intervals -// They then do Bresenham stuff themselves - -StepTicker* StepTicker::global_step_ticker; - -StepTicker::StepTicker(){ - StepTicker::global_step_ticker = this; +StepTicker::StepTicker() +{ + instance = this; // setup the Singleton instance of the stepticker uint32_t PCLK = SystemCoreClock; uint32_t prescale = PCLK / 10000000; @@ -43,59 +46,55 @@ StepTicker::StepTicker(){ while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER0_RST & 31)))){} /* Configure Timer 0 */ LPC_TIMER0->CTCR = 0x0; // timer mode - LPC_TIMER0->TCR = 0; // Disable interrupt - LPC_TIMER0->PR = prescale - 1; - LPC_TIMER0->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER0->MCR |= 3; // Match on MR0, reset on MR0 - NVIC_SetPriority(TIMER0_IRQn,0); - - /* Enable timer 1 clock and reset it */ - LPC_CCU1->CLKCCU[CLK_MX_TIMER1].CFG |= 1; - LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer - while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER1_RST & 31)))){} - /* Configure Timer 0 */ - LPC_TIMER1->CTCR = 0x0; // timer mode - LPC_TIMER1->TCR = 0; // Disable interrupt - LPC_TIMER1->PR = prescale - 1; - LPC_TIMER1->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER1->MCR |= 5; // match on Mr0, stop on match - - //Configure RIT Timer for 1ms acceleration ticks - LPC_CCU1->CLKCCU[CLK_MX_RITIMER].CFG = 0x07; - LPC_RITIMER->COMPVAL = 0xFFFFFFFF; - LPC_RITIMER->MASK = 0x00000000; - LPC_RITIMER->CTRL = 0x0C; - LPC_RITIMER->COUNTER = 0x00000000; - LPC_RITIMER->COMPVAL = (uint32_t)(((SystemCoreClock / 1000000L) * 1000)-1); // 1ms period - LPC_RITIMER->CTRL |= 0; //disable RIT Timer + LPC_TIMER0->TCR = 0; // Disable interrupt + LPC_TIMER0->PR = prescale - 1; + LPC_TIMER0->MR[0] = 10000000; // Initial dummy value for Match Register + LPC_TIMER0->MCR |= 3; // Match on MR0, reset on MR0 + NVIC_SetPriority(TIMER0_IRQn,0); + + /* Enable timer 1 clock and reset it */ + LPC_CCU1->CLKCCU[CLK_MX_TIMER1].CFG |= 1; + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer + while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER1_RST & 31)))){} + /* Configure Timer 0 */ + LPC_TIMER1->CTCR = 0x0; // timer mode + LPC_TIMER1->TCR = 0; // Disable interrupt + LPC_TIMER1->PR = prescale - 1; + LPC_TIMER1->MR[0] = 10000000; // Initial dummy value for Match Register + LPC_TIMER1->MCR |= 5; // match on Mr0, stop on match // Default start values - this->a_move_finished = false; - this->do_move_finished = 0; - this->unstep.reset(); this->set_frequency(100000); this->set_reset_delay(5); - this->set_acceleration_ticks_per_second(1000); - this->num_motors= 0; - this->active_motor.reset(); - this->tick_cnt= 0; + //TODO REMOVE this->set_unstep_time(100); + + this->unstep.reset(); + this->num_motors = 0; - /* Enable Timers IRQ */ - NVIC_EnableIRQ(TIMER1_IRQn); // Enable interrupt handler - NVIC_EnableIRQ(RITIMER_IRQn); + this->running = false; + this->current_block = nullptr; + #ifdef STEPTICKER_DEBUG_PIN + // setup debug pin if defined + stepticker_debug_pin.output(); + stepticker_debug_pin= 0; + #endif } StepTicker::~StepTicker() { } -// Called when everything is setup and interrupts can start -void StepTicker::start() { - +//called when everything is setup and interrupts can start +void StepTicker::start() +{ + NVIC_EnableIRQ(TIMER0_IRQn); // Enable interrupt handler + NVIC_EnableIRQ(TIMER1_IRQn); // Enable interrupt handler + current_tick= 0; } // Set the base stepping frequency -void StepTicker::set_frequency( float frequency ){ +void StepTicker::set_frequency( float frequency ) +{ this->frequency = frequency; this->period_us = (int)lrint(1000000/frequency); @@ -110,104 +109,136 @@ void StepTicker::set_reset_delay( float microseconds ){ this->reset_delay_us = (int)lrint(microseconds); /* Update Timer 1 Match register */ LPC_TIMER1->MR[0] = this->reset_delay_us; -} - -// This is the number of acceleration ticks per second -void StepTicker::set_acceleration_ticks_per_second(uint32_t acceleration_ticks_per_second) { - this->acceleration_period_us = (int)lrint(1000000/acceleration_ticks_per_second); - this->synchronize_acceleration(false); -} - -// Synchronize the acceleration timer, and optionally schedule it to fire now -void StepTicker::synchronize_acceleration(bool fire_now) { - LPC_RITIMER->COUNTER = 0; - if(fire_now){ - NVIC_SetPendingIRQ(RITIMER_IRQn); - }else{ - if(NVIC_GetPendingIRQ(RITIMER_IRQn)) { - // clear pending interrupt so it does not interrupt immediately - LPC_RITIMER->CTRL |= 1L; // also clear the interrupt in case it fired - NVIC_ClearPendingIRQ(RITIMER_IRQn); - } - } -} - -// Call signal_move_finished() on each active motor that asked to be signaled. We do this instead of inside of tick() so that -// all tick()s are called before we do the move finishing -void StepTicker::signal_a_move_finished(){ - for (int motor = 0; motor < num_motors; motor++){ - if (this->active_motor[motor] && this->motor[motor]->is_move_finished){ - this->motor[motor]->signal_move_finished(); - } - } + // TODO check that the unstep time is less than the step period, if not slow down step ticker } // Reset step pins on any motor that was stepped -inline void StepTicker::unstep_tick(){ - for (int i = 0; i < num_motors; i++) { - if(this->unstep[i]){ +void StepTicker::unstep_tick() +{ + for (int i = 0; i < num_motors; i++) + { + if(this->unstep[i]) + { this->motor[i]->unstep(); } } this->unstep.reset(); } -// Acceleration timer interrupt handler -extern "C" void RITIMER_IRQHandler (void) -{ - - LPC_RITIMER->CTRL |= 1L; - //LPC_TIMER2->IR |= 1 << 0; - StepTicker::global_step_ticker->acceleration_tick(); - //NVIC_ClearPendingIRQ(TIMER2_IRQn); - LPC_RITIMER->COUNTER = 0; - SEGGER_SYSVIEW_RecordExitISR(); -} // Unstep timer interrupt handler extern "C" void TIMER1_IRQHandler (void) { // LPC_TIMER1->IR |= 1 << 0; - StepTicker::global_step_ticker->unstep_tick(); + StepTicker::getInstance()->unstep_tick(); NVIC_ClearPendingIRQ(TIMER1_IRQn); // SEGGER_SYSVIEW_RecordExitISR(); } -// Step timer interrupt handler +// The actual interrupt handler where we do all the work - the step_tick extern "C" void TIMER0_IRQHandler (void) { // SEGGER_RTT_LOCK(); // SEGGER_SYSVIEW_RecordEnterISR(); LPC_TIMER0->IR |= 1 << 0; - StepTicker::global_step_ticker->step_tick(); + StepTicker::getInstance()->step_tick(); NVIC_ClearPendingIRQ(TIMER0_IRQn); // SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } -// RIT Timer interrupt handler -void StepTicker::acceleration_tick() { - // call registered acceleration handlers - for (size_t i = 0; i < acceleration_tick_handlers.size(); ++i) { - acceleration_tick_handlers[i](); - } +extern "C" void PendSV_Handler(void) +{ + StepTicker::getInstance()->handle_finish(); } -void StepTicker::step_tick(void){ - // Reset interrupt register - tick_cnt++; // count number of ticks +// slightly lower priority than TIMER0, the whole end of block/start of block is done here allowing the timer to continue ticking +void StepTicker::handle_finish (void) +{ + // all moves finished signal block is finished + if(finished_fnc) finished_fnc(); +} + +// step clock +void StepTicker::step_tick (void) +{ + //SET_STEPTICKER_DEBUG_PIN(running ? 1 : 0); + + // if nothing has been setup we ignore the ticks + if(!running){ + // check if anything new available + if(THECONVEYOR->get_next_block(¤t_block)) { // returns false if no new block is available + running= start_next_block(); // returns true if there is at least one motor with steps to issue + if(!running) return; + }else{ + return; + } + } + + if(THEKERNEL->is_halted()) { + running= false; + current_tick = 0; + current_block= nullptr; + return; + } + + bool still_moving= false; + // foreach motor, if it is active see if time to issue a step to that motor + for (uint8_t m = 0; m < num_motors; m++) { + if(current_block->tick_info[m].steps_to_move == 0) continue; // not active + + current_block->tick_info[m].steps_per_tick += current_block->tick_info[m].acceleration_change; + + if(current_tick == current_block->tick_info[m].next_accel_event) { + if(current_tick == current_block->accelerate_until) { // We are done accelerating, deceleration becomes 0 : plateau + current_block->tick_info[m].acceleration_change = 0; + if(current_block->decelerate_after < current_block->total_move_ticks) { + current_block->tick_info[m].next_accel_event = current_block->decelerate_after; + if(current_tick != current_block->decelerate_after) { // We are plateauing + // steps/sec / tick frequency to get steps per tick + current_block->tick_info[m].steps_per_tick = current_block->tick_info[m].plateau_rate; + } + } + } + + if(current_tick == current_block->decelerate_after) { // We start decelerating + current_block->tick_info[m].acceleration_change = current_block->tick_info[m].deceleration_change; + } + } + + // protect against rounding errors and such + if(current_block->tick_info[m].steps_per_tick <= 0) { + current_block->tick_info[m].counter = STEPTICKER_FPSCALE; // we force completion this step by setting to 1.0 + current_block->tick_info[m].steps_per_tick = 0; + } + + current_block->tick_info[m].counter += current_block->tick_info[m].steps_per_tick; - // Step pins NOTE takes 1.2us when nothing to step, 1.8-2us for one motor stepped and 2.6us when two motors stepped, 3.167us when three motors stepped - for (uint32_t motor = 0; motor < num_motors; motor++){ - // send tick to all active motors - if(this->active_motor[motor] && this->motor[motor]->tick()){ + if(current_block->tick_info[m].counter >= STEPTICKER_FPSCALE) { // >= 1.0 step time + current_block->tick_info[m].counter -= STEPTICKER_FPSCALE; // -= 1.0F; + ++current_block->tick_info[m].step_count; + + // step the motor + bool ismoving= motor[m]->step(); // returns false if the moving flag was set to false externally (probes, endstops etc) // we stepped so schedule an unstep - this->unstep[motor]= 1; + unstep.set(m); + + if(!ismoving || current_block->tick_info[m].step_count == current_block->tick_info[m].steps_to_move) { + // done + current_block->tick_info[m].steps_to_move = 0; + motor[m]->stop_moving(); // let motor know it is no longer moving + } } + + // see if any motors are still moving after this tick + if(motor[m]->is_moving()) still_moving= true; } + // do this after so we start at tick 0 + current_tick++; // count number of ticks + // We may have set a pin on in this tick, now we reset the timer to set it off // Note there could be a race here if we run another tick before the unsteps have happened, // right now it takes about 3-4us but if the unstep were near 10uS or greater it would be an issue @@ -217,51 +248,70 @@ void StepTicker::step_tick(void){ LPC_TIMER1->TCR = 0x3; // reset LPC_TIMER1->TCR = 1; // enable = 1, reset = 0 } - - // Check if any moves finished - if(this->a_move_finished) { - this->a_move_finished= false; - this->do_move_finished++; // Note this is an atomic variable because it is updated in two interrupts of different priorities so can be pre-empted - } - // If a move finished in this tick, we have to tell the actuator to act accordingly - if(this->do_move_finished.load() > 0){ - if(this->do_move_finished.load() > 0) { - this->do_move_finished--; - this->signal_a_move_finished(); + // see if any motors are still moving + if(!still_moving) { + //SET_STEPTICKER_DEBUG_PIN(0); + + // all moves finished + current_tick = 0; + + // get next block + // do it here so there is no delay in ticks + THECONVEYOR->block_finished(); + + if(THECONVEYOR->get_next_block(¤t_block)) { // returns false if no new block is available + running= start_next_block(); // returns true if there is at least one motor with steps to issue + + }else{ + current_block= nullptr; + running= false; } - } -} -// returns index of the stepper motor in the array and bitset -int StepTicker::register_motor(StepperMotor* motor) -{ - this->motor.push_back(motor); - this->num_motors= this->motor.size(); - return this->num_motors-1; + // all moves finished + // we delegate the slow stuff to the pendsv handler which will run as soon as this interrupt exits + //NVIC_SetPendingIRQ(PendSV_IRQn); this doesn't work + //SCB->ICSR = 0x10000000; // SCB_ICSR_PENDSVSET_Msk; + } } -// activate the specified motor, must have been registered -void StepTicker::add_motor_to_active_list(StepperMotor* motor) +// only called from the step tick ISR (single consumer) +bool StepTicker::start_next_block() { - bool enabled= active_motor.any(); // see if interrupt was previously enabled - active_motor[motor->index]= 1; - if(!enabled) { - /* Enable Step Timer (Timer 0) */ - LPC_TIMER0->TCR = 0x2; // reset - LPC_TIMER0->TCR = 0x1; // enable - NVIC_EnableIRQ(TIMER0_IRQn); + if(current_block == nullptr) return false; + + bool ok= false; + // need to prepare each active motor + for (uint8_t m = 0; m < num_motors; m++) { + if(current_block->tick_info[m].steps_to_move == 0) continue; + + ok= true; // mark at least one motor is moving + // set direction bit here + // NOTE this would be at least 10us before first step pulse. + // TODO does this need to be done sooner, if so how without delaying next tick + motor[m]->set_direction(current_block->direction_bits[m]); + motor[m]->start_moving(); // also let motor know it is moving now } -} -// Remove a stepper from the list of active motors -void StepTicker::remove_motor_from_active_list(StepperMotor* motor){ - active_motor[motor->index]= 0; - // If we have no motor to work on, disable the whole interrupt - if(this->active_motor.none()){ - /* Disable step Timer (Timer 0) */ - NVIC_DisableIRQ(TIMER0_IRQn); - tick_cnt= 0; + current_tick= 0; + + if(ok) { + //SET_STEPTICKER_DEBUG_PIN(1); + return true; + + }else{ + // this is an edge condition that should never happen, but we need to discard this block if it ever does + // basically it is a block that has zero steps for all motors + THECONVEYOR->block_finished(); } + + return false; } + +// returns index of the stepper motor in the array and bitset +int StepTicker::register_motor(StepperMotor* m) +{ + motor[num_motors++] = m; + return num_motors - 1; +} diff --git a/src/libs/StepTicker.h b/src/libs/StepTicker.h index 6d63f4fc..f23d19f2 100644 --- a/src/libs/StepTicker.h +++ b/src/libs/StepTicker.h @@ -7,67 +7,64 @@ -#ifndef STEPTICKER_H -#define STEPTICKER_H +#pragma once #include -#include +#include #include #include #include +#include "ActuatorCoordinates.h" +#include "TSRingBuffer.h" + #include "mbed.h" class StepperMotor; +class Block; + +// handle 2.30 Fixed point +#define STEPTICKER_FPSCALE (1<<30) +#define STEPTICKER_TOFP(x) ((int32_t)roundf((float)(x)*STEPTICKER_FPSCALE)) +#define STEPTICKER_FROMFP(x) ((float)(x)/STEPTICKER_FPSCALE) class StepTicker{ public: - static StepTicker* global_step_ticker; - StepTicker(); ~StepTicker(); void set_frequency( float frequency ); - void signal_a_move_finished(); - void set_reset_delay( float seconds ); + void set_reset_delay( float seconds ); + //TODO REMOVE void set_unstep_time( float microseconds ); int register_motor(StepperMotor* motor); - void add_motor_to_active_list(StepperMotor* motor); - void remove_motor_from_active_list(StepperMotor* motor); - void set_acceleration_ticks_per_second(uint32_t acceleration_ticks_per_second); float get_frequency() const { return frequency; } void unstep_tick(); - void step_tick(); - uint32_t get_tick_cnt() const { return tick_cnt; } - uint32_t ticks_since(uint32_t last) const { return (tick_cnt>=last) ? tick_cnt-last : (UINT32_MAX-last) + tick_cnt + 1; } - - void register_acceleration_tick_handler(std::function cb){ - acceleration_tick_handlers.push_back(cb); - } - void acceleration_tick(); - void synchronize_acceleration(bool fire_now); - void schedule_unstep(int motor); + const Block *get_current_block() const { return current_block; } + void step_tick (void); + void handle_finish (void); void start(); - friend class StepperMotor; + // whatever setup the block should register this to know when it is done + std::function finished_fnc{nullptr}; - // TOADDBACKĀ was private - volatile uint32_t tick_cnt; - uint32_t period_us; - uint32_t reset_delay_us; - private: + static StepTicker *getInstance() { return instance; } + uint32_t reset_delay_us; - float frequency; - uint32_t acceleration_period_us; - std::vector> acceleration_tick_handlers; - std::vector motor; - std::bitset<32> active_motor; // limit to 32 motors - std::bitset<32> unstep; // limit to 32 motors - std::atomic_uchar do_move_finished; - uint8_t num_motors; - volatile bool a_move_finished; + private: + static StepTicker *instance; -}; + bool start_next_block(); + float frequency; + uint32_t period_us; + std::array motor; + std::bitset unstep; + Block *current_block; + uint32_t current_tick{0}; -#endif + struct { + volatile bool running:1; + uint8_t num_motors:4; + }; +}; diff --git a/src/libs/StepperMotor.cpp b/src/libs/StepperMotor.cpp index 98d19b57..cdc533af 100644 --- a/src/libs/StepperMotor.cpp +++ b/src/libs/StepperMotor.cpp @@ -6,44 +6,16 @@ */ #include "StepperMotor.h" -#include "Kernel.h" +#include "libs/Module.h" +#include "libs/Kernel.h" //#include "MRI_Hooks.h" #include "StepTicker.h" #include #include "mbed.h" -// A StepperMotor represents an actual stepper motor. It is used to generate steps that move the actual motor at a given speed - -StepperMotor::StepperMotor() -{ - init(); -} - StepperMotor::StepperMotor(Pin &step, Pin &dir, Pin &en) : step_pin(step), dir_pin(dir), en_pin(en) { - init(); - enable(false); - // TOADDBACK set_high_on_debug(en.port_number, en.pin); -} - -StepperMotor::~StepperMotor() -{ -} - -void StepperMotor::init() -{ - // register this motor with the step ticker, and get its index in that array and bit position - this->index= THEKERNEL->step_ticker->register_motor(this); - this->moving = false; - this->fx_counter = 0; - this->fx_ticks_per_step = 0xFFFFF000UL; // some big number so we don't start stepping before it is set - this->stepped = 0; - this->steps_to_move = 0; - this->is_move_finished = false; - this->last_step_tick_valid= false; - this->last_step_tick= 0; - this->force_finish= false; steps_per_mm = 1.0F; max_rate = 50.0F; @@ -51,162 +23,43 @@ void StepperMotor::init() last_milestone_steps = 0; last_milestone_mm = 0.0F; current_position_steps= 0; - signal_step= 0; -} + moving= false; + acceleration= NAN; + selected= true; + enable(false); + unstep(); // initialize step pin + set_direction(false); // initialize direction pin -// This is called ( see the .h file, we had to put a part of things there for obscure inline reasons ) when a step has to be generated -// we also here check if the move is finished etc .. -// This is in highest priority interrupt so cannot be pre-empted -void StepperMotor::step() -{ - // ignore if we are still processing the end of a block - if(this->is_move_finished) return; - - if(!this->force_finish) { - // output to pins 37t - this->step_pin.set( 1 ); - - // move counter back 11t - this->fx_counter -= this->fx_ticks_per_step; - - // we have moved a step 9t - this->stepped++; - - // keep track of actuators actual position in steps - this->current_position_steps += (this->direction ? -1 : 1); - - // we may need to callback on a specific step, usually used to synchronize deceleration timer - if(this->signal_step != 0 && this->stepped == this->signal_step) { - THEKERNEL->step_ticker->synchronize_acceleration(true); - this->signal_step= 0; - } - } - - // Is this move finished ? - if( this->force_finish || this->stepped == this->steps_to_move) { - // Mark it as finished, then StepTicker will call signal_move_finished() - // This is so we don't call that before all the steps have been generated for this tick() - this->is_move_finished = true; - THEKERNEL->step_ticker->a_move_finished= true; - this->last_step_tick= THEKERNEL->step_ticker->get_tick_cnt(); // remember when last step was - if(this->force_finish) this->steps_to_move = stepped; - } -} - -// Does a manual step pulse, used for direct encoder control of a stepper -void StepperMotor::manual_step(bool dir) -{ - if(!enabled) enable(true); - - // set direction if needed - if(this->direction != dir) { - this->direction= dir; - this->dir_pin.set(dir); - wait_us(1); - } - - // set step pin - this->step_pin.set(1); - - // schedule for unstep - THEKERNEL->step_ticker->schedule_unstep(this->index); - - // keep track of actuators actual position in steps - this->current_position_steps += (dir ? -1 : 1); + this->register_for_event(ON_HALT); + this->register_for_event(ON_ENABLE); } -// If the move is finished, the StepTicker will call this ( because we asked it to in tick() ) -void StepperMotor::signal_move_finished() +StepperMotor::~StepperMotor() { - // work is done ! 8t - this->moving = false; - this->steps_to_move = 0; - - // signal it to whatever cares - // in this call a new block may start, new moves set and new speeds - this->end_hook->call(); - - // We only need to do this if we were not instructed to move - if( !this->moving ) { - this->update_exit_tick(); - } - - this->is_move_finished = false; + THEKERNEL->unregister_for_event(ON_HALT, this); + THEKERNEL->unregister_for_event(ON_ENABLE, this); } -// This is just a way not to check for ( !this->moving || this->fx_ticks_per_step == 0 ) at every tick() -void StepperMotor::update_exit_tick() +void StepperMotor::on_halt(void *argument) { - if( !this->moving || this->steps_to_move == 0 ) { - // No more ticks will be received and no more events from StepTicker - THEKERNEL->step_ticker->remove_motor_from_active_list(this); - } else { - // we will now get ticks and StepTIcker will send us events - THEKERNEL->step_ticker->add_motor_to_active_list(this); + if(argument == nullptr) { + enable(false); + moving= false; } } -// Instruct the StepperMotor to move a certain number of steps -StepperMotor* StepperMotor::move( bool direction, unsigned int steps, float initial_speed) +void StepperMotor::on_enable(void *argument) { - this->dir_pin.set(direction); - this->direction = direction; - this->force_finish= false; - - // How many steps we have to move until the move is done - this->steps_to_move = steps; - - // Zero our tool counters - this->stepped = 0; - this->fx_ticks_per_step = 0xFFFFF000UL; // some big number so we don't start stepping before it is set again - if(this->last_step_tick_valid) { - // we set this based on when the last step was, thus compensating for missed ticks - uint32_t ts= THEKERNEL->step_ticker->ticks_since(this->last_step_tick); - // if an axis stops too soon then we can get a huge number of ticks here which causes problems, so if the number of ticks is too great we ignore them - // example of when this happens is when one axis is going very slow and the min 20steps/sec kicks in, the axis will reach its target much sooner leaving a long gap - // until the end of the block. - // TODO we may need to set this based on the current step rate, trouble is we don't know what that is yet, we could use the last fx_ticks_per_step as a guide - if(ts > 5) ts= 5; // limit to 50us catch up around 1-2 steps - else if(ts > 15) ts= 0; // no way to know what the delay was - this->fx_counter= ts*fx_increment; - }else{ - this->fx_counter = 0; // set to zero as there was no step last block + // argument is a uin32_t where bit0 is on or off, and bit 1:X, 2:Y, 3:Z, 4:A, 5:B, 6:C etc + // for now if bit0 is 1 we turn all on, if 0 we turn all off otherwise we turn selected axis off + uint32_t bm= (uint32_t)argument; + if(bm == 0x01) { + enable(true); + + }else if(bm == 0 || ((bm&0x01) == 0 && ((bm&(0x02< 0 ) { - if(initial_speed >= 0.0F) set_speed(initial_speed); - this->moving = true; - } else { - this->moving = false; - } - this->update_exit_tick(); - return this; -} - -// Set the speed at which this stepper moves in steps/sec, should be called set_step_rate() -// we need to make sure that we have a minimum speed here and that it fits the 32bit fixed point fx counters -// Note nothing will really ever go as slow as the minimum speed here, it is just forced to avoid bad errors -// fx_ticks_per_step is what actually sets the step rate, it is fixed point 18.14 -StepperMotor* StepperMotor::set_speed( float speed ) -{ - if(speed < minimum_step_rate) { - speed= minimum_step_rate; - } - - // if(speed <= 0.0F) { // we can't actually do 0 but we can get close, need to avoid divide by zero later on - // this->fx_ticks_per_step= 0xFFFFFFFFUL; // 0.381 steps/sec - // this->steps_per_second = THEKERNEL->step_ticker->get_frequency() / (this->fx_ticks_per_step >> fx_shift); - // return; - // } - - // How many steps we must output per second - this->steps_per_second = speed; - - // set the new speed, NOTE this can be pre-empted by stepticker so the following write needs to be atomic - this->fx_ticks_per_step= floor(fx_increment * THEKERNEL->step_ticker->get_frequency() / speed); - return this; } void StepperMotor::change_steps_per_mm(float new_steps) @@ -223,8 +76,21 @@ void StepperMotor::change_last_milestone(float new_milestone) current_position_steps = last_milestone_steps; } -int StepperMotor::steps_to_target(float target) +void StepperMotor::set_last_milestones(float mm, int32_t steps) +{ + last_milestone_mm= mm; + last_milestone_steps= steps; + current_position_steps= last_milestone_steps; +} + +void StepperMotor::update_last_milestones(float mm, int32_t steps) +{ + last_milestone_steps += steps; + last_milestone_mm = mm; +} + +int32_t StepperMotor::steps_to_target(float target) { - int target_steps = lroundf(target * steps_per_mm); + int32_t target_steps = lroundf(target * steps_per_mm); return target_steps - last_milestone_steps; } diff --git a/src/libs/StepperMotor.h b/src/libs/StepperMotor.h index 87f0ab92..74afd3e5 100644 --- a/src/libs/StepperMotor.h +++ b/src/libs/StepperMotor.h @@ -5,70 +5,61 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef STEPPERMOTOR_H -#define STEPPERMOTOR_H +#pragma once -#include "libs/Hook.h" +#include "Module.h" #include "Pin.h" #include #include -class StepTicker; -class Hook; - -class StepperMotor { +class StepperMotor : public Module { public: StepperMotor(); StepperMotor(Pin& step, Pin& dir, Pin& en); ~StepperMotor(); - void step(); - inline void unstep() { step_pin.set(0); }; - void manual_step(bool dir); + void set_motor_id(uint8_t id) { motor_id= id; } + uint8_t get_motor_id() const { return motor_id; } + + // called from step ticker ISR + inline bool step() { step_pin.set(1); current_position_steps += (direction?-1:1); return moving; } + // called from unstep ISR + inline void unstep() { step_pin.set(0); } + // called from step ticker ISR + inline void set_direction(bool f) { dir_pin.set(f); direction= f; } - inline void enable(bool state) { enabled= state; en_pin.set(!state); }; + void enable(bool state) { en_pin.set(!state); }; + //bool is_enabled() const { return !en_pin.get(); }; + bool is_moving() const { return moving; }; + void start_moving() { moving= true; } + void stop_moving() { moving= false; } + + void manual_step(bool dir); - bool is_moving() const { return moving; } bool which_direction() const { return direction; } - void move_finished(); - StepperMotor* move( bool direction, unsigned int steps, float initial_speed= -1.0F); - void signal_move_finished(); - StepperMotor* set_speed( float speed ); - void set_moved_last_block(bool flg) { last_step_tick_valid= flg; } - void update_exit_tick(); float get_steps_per_second() const { return steps_per_second; } float get_steps_per_mm() const { return steps_per_mm; } void change_steps_per_mm(float); void change_last_milestone(float); + void set_last_milestones(float, int32_t); + void update_last_milestones(float mm, int32_t steps); float get_last_milestone(void) const { return last_milestone_mm; } + int32_t get_last_milestone_steps(void) const { return last_milestone_steps; } float get_current_position(void) const { return (float)current_position_steps/steps_per_mm; } + uint32_t get_current_step(void) const { return current_position_steps; } float get_max_rate(void) const { return max_rate; } void set_max_rate(float mr) { max_rate= mr; } - float get_min_rate(void) const { return minimum_step_rate; } - void set_min_rate(float mr) { minimum_step_rate= mr; } - - int steps_to_target(float); - uint32_t get_steps_to_move() const { return steps_to_move; } - uint32_t get_stepped() const { return stepped; } - void force_finish_move() { force_finish= true; } + void set_acceleration(float a) { acceleration= a; } + float get_acceleration() const { return acceleration; } + bool is_selected() const { return selected; } + void set_selected(bool b) { selected= b; } - template void attach( T *optr, uint32_t ( T::*fptr )( uint32_t ) ){ - Hook* hook = new Hook(); - hook->attach(optr, fptr); - this->end_hook = hook; - } - - friend class StepTicker; - friend class Stepper; - friend class Planner; - friend class Robot; + int32_t steps_to_target(float); private: - void init(); - - int index; - Hook* end_hook; + void on_halt(void *argument); + void on_enable(void *argument); Pin step_pin; Pin dir_pin; @@ -77,7 +68,7 @@ class StepperMotor { float steps_per_second; float steps_per_mm; float max_rate; // this is not really rate it is in mm/sec, misnamed used in Robot and Extruder - float minimum_step_rate; // this is the minimum step_rate in steps/sec for this motor for this block + float acceleration; volatile int32_t current_position_steps; int32_t last_milestone_steps; @@ -95,26 +86,9 @@ class StepperMotor { uint32_t fx_ticks_per_step; volatile struct { - volatile bool is_move_finished:1; // Whether the move just finished + uint8_t motor_id:8; + volatile bool direction:1; volatile bool moving:1; - volatile bool force_finish:1; // set to force a move to finish early - bool direction:1; - bool last_step_tick_valid:1; // set if the last step tick time is valid (ie the motor moved last block) - bool enabled:1; - }; - - // Called a great many times per second, to step if we have to now - inline bool tick() { - // increase the ( 32 fixed point 18:14 ) counter by one tick 11t - fx_counter += fx_increment; - - // if we are to step now - if (fx_counter >= fx_ticks_per_step){ - step(); - return true; - } - return false; + bool selected:1; }; }; - -#endif diff --git a/src/modules/communication/GcodeDispatch.cpp b/src/modules/communication/GcodeDispatch.cpp index 0e00b9db..0af41317 100644 --- a/src/modules/communication/GcodeDispatch.cpp +++ b/src/modules/communication/GcodeDispatch.cpp @@ -151,9 +151,13 @@ void GcodeDispatch::on_console_line_received(void *line) if(THEKERNEL->is_halted()) { // we ignore all commands until M999, unless it is in the exceptions list (like M105 get temp) if(gcode->has_m && gcode->m == 999) { - THEKERNEL->call_event(ON_HALT, (void *)1); // clears on_halt - - // fall through and pass onto other modules + if(THEKERNEL->is_halted()) { + THEKERNEL->call_event(ON_HALT, (void *)1); // clears on_halt + new_message.stream->printf("WARNING: After HALT you should HOME as position is currently unknown\n"); + } + new_message.stream->printf("ok\n"); + delete gcode; + continue; }else if(!is_allowed_mcode(gcode->m)) { // ignore everything, return error string to host @@ -284,7 +288,7 @@ void GcodeDispatch::on_console_line_received(void *line) } case 500: // M500 save volatile settings to config-override - THEKERNEL->conveyor->wait_for_empty_queue(); //just to be safe as it can take a while to run + THEKERNEL->conveyor->wait_for_idle(); //just to be safe as it can take a while to run //remove(THEKERNEL->config_override_filename()); // seems to cause a hang every now and then // TOADDBACK :Ā __disable_irq(); { @@ -333,7 +337,6 @@ void GcodeDispatch::on_console_line_received(void *line) gcode->add_nl= true; break; // fall through to process by modules } - } } diff --git a/src/modules/communication/utils/Gcode.cpp b/src/modules/communication/utils/Gcode.cpp index e4e8cd68..1de67536 100644 --- a/src/modules/communication/utils/Gcode.cpp +++ b/src/modules/communication/utils/Gcode.cpp @@ -23,7 +23,6 @@ Gcode::Gcode(const string &command, StreamOutput *stream, bool strip) this->add_nl= false; this->is_error= false; this->stream= stream; - this->millimeters_of_travel = 0.0F; prepare_cached_values(strip); this->stripped= strip; } @@ -39,7 +38,6 @@ Gcode::~Gcode() Gcode::Gcode(const Gcode &to_copy) { this->command = strdup(to_copy.command); // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store - this->millimeters_of_travel = to_copy.millimeters_of_travel; this->has_m = to_copy.has_m; this->has_g = to_copy.has_g; this->m = to_copy.m; @@ -55,7 +53,6 @@ Gcode &Gcode::operator= (const Gcode &to_copy) { if( this != &to_copy ) { this->command = strdup(to_copy.command); // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store - this->millimeters_of_travel = to_copy.millimeters_of_travel; this->has_m = to_copy.has_m; this->has_g = to_copy.has_g; this->m = to_copy.m; @@ -178,15 +175,12 @@ void Gcode::prepare_cached_values(bool strip) if( this->has_letter('G') ) { this->has_g = true; this->g = this->get_int('G', &p); - } else { this->has_g = false; } - if( this->has_letter('M') ) { this->has_m = true; this->m = this->get_int('M', &p); - } else { this->has_m = false; } diff --git a/src/modules/communication/utils/Gcode.h b/src/modules/communication/utils/Gcode.h index aa718f91..ad124e2e 100644 --- a/src/modules/communication/utils/Gcode.h +++ b/src/modules/communication/utils/Gcode.h @@ -36,7 +36,6 @@ class Gcode { // FIXME these should be private unsigned int m; unsigned int g; - float millimeters_of_travel; struct { bool add_nl:1; diff --git a/src/modules/robot/Block.cpp b/src/modules/robot/Block.cpp index 96e8a8f6..58c66588 100644 --- a/src/modules/robot/Block.cpp +++ b/src/modules/robot/Block.cpp @@ -15,13 +15,18 @@ #include "Conveyor.h" #include "Gcode.h" #include "libs/StreamOutputPool.h" -#include "Stepper.h" +#include "StepTicker.h" #include "mri.h" using std::string; #include +#define STEP_TICKER_FREQUENCY THEKERNEL->step_ticker->get_frequency() +#define STEP_TICKER_FREQUENCY_2 (STEP_TICKER_FREQUENCY*STEP_TICKER_FREQUENCY) + +uint8_t Block::n_actuators= 0; + // A block represents a movement, it's length for each stepper motor, and the corresponding acceleration curves. // It's stacked on a queue, and that queue is then executed in order, to move the motors. // Most of the accel math is also done in this class @@ -34,55 +39,73 @@ Block::Block() void Block::clear() { - //commands.clear(); - //travel_distances.clear(); - gcodes.clear(); - std::vector().swap(gcodes); // this resizes the vector releasing its memory + is_ready = false; this->steps.fill(0); steps_event_count = 0; - nominal_rate = 0; + nominal_rate = 0.0F; nominal_speed = 0.0F; millimeters = 0.0F; entry_speed = 0.0F; exit_speed = 0.0F; - rate_delta = 0.0F; - acceleration = 100.0F; // we don't want to get devide by zeroes if this is not set - initial_rate = -1; - final_rate = -1; + acceleration = 100.0F; // we don't want to get divide by zeroes if this is not set + initial_rate = 0.0F; accelerate_until = 0; decelerate_after = 0; direction_bits = 0; recalculate_flag = false; nominal_length_flag = false; max_entry_speed = 0.0F; - is_ready = false; - times_taken = 0; + is_ticking = false; + is_g123 = false; + locked = false; + s_value = 0.0F; + + acceleration_per_tick= 0; + deceleration_per_tick= 0; + total_move_ticks= 0; + if(tick_info.size() != n_actuators) { + tick_info.resize(n_actuators); + } + for(auto &i : tick_info) { + i.steps_per_tick= 0; + i.counter= 0; + i.acceleration_change= 0; + i.deceleration_change= 0; + i.plateau_rate= 0; + i.steps_to_move= 0; + i.step_count= 0; + i.next_accel_event= 0; + } } -void Block::debug() +void Block::debug() const { - THEKERNEL->streams->printf("%p: steps:X%04lu Y%04lu Z%04lu(max:%4lu) nominal:r%10lu/s%6.1f mm:%9.6f rdelta:%8f acc:%5lu dec:%5lu rates:%10lu>%10lu entry/max: %10.4f/%10.4f taken:%d ready:%d recalc:%d nomlen:%d\r\n", - this, - this->steps[0], - this->steps[1], - this->steps[2], + THEKERNEL->streams->printf("%p: steps-X:%lu Y:%lu Z:%lu ", this, this->steps[0], this->steps[1], this->steps[2]); + for (size_t i = E_AXIS; i < n_actuators; ++i) { + THEKERNEL->streams->printf("E%d:%lu ", i-E_AXIS, this->steps[i]); + } + THEKERNEL->streams->printf("(max:%lu) nominal:r%1.4f/s%1.4f mm:%1.4f acc:%1.2f accu:%lu decu:%lu ticks:%lu rates:%1.4f entry/max:%1.4f/%1.4f exit:%1.4f primary:%d ready:%d locked:%d ticking:%d recalc:%d nomlen:%d time:%f\r\n", this->steps_event_count, this->nominal_rate, this->nominal_speed, this->millimeters, - this->rate_delta, + this->acceleration, this->accelerate_until, this->decelerate_after, + this->total_move_ticks, this->initial_rate, - this->final_rate, this->entry_speed, this->max_entry_speed, - this->times_taken, + this->exit_speed, + this->primary_axis, this->is_ready, + this->locked, + this->is_ticking, recalculate_flag ? 1 : 0, - nominal_length_flag ? 1 : 0 + nominal_length_flag ? 1 : 0, + total_move_ticks/STEP_TICKER_FREQUENCY ); } @@ -99,60 +122,100 @@ void Block::debug() void Block::calculate_trapezoid( float entryspeed, float exitspeed ) { // if block is currently executing, don't touch anything! - if (times_taken) - return; - - // The planner passes us factors, we need to transform them in rates - this->initial_rate = ceilf(this->nominal_rate * entryspeed / this->nominal_speed); // (step/s) - this->final_rate = ceilf(this->nominal_rate * exitspeed / this->nominal_speed); // (step/s) - - // How many steps to accelerate and decelerate - float acceleration_per_second = this->rate_delta * THEKERNEL->acceleration_ticks_per_second; // ( step/s^2) - int accelerate_steps = ceilf( this->estimate_acceleration_distance( this->initial_rate, this->nominal_rate, acceleration_per_second ) ); - int decelerate_steps = floorf( this->estimate_acceleration_distance( this->nominal_rate, this->final_rate, -acceleration_per_second ) ); - - // Calculate the size of Plateau of Nominal Rate ( during which we don't accelerate nor decelerate, but just cruise ) - int plateau_steps = this->steps_event_count - accelerate_steps - decelerate_steps; - - // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will - // have to use intersection_distance() to calculate when to abort acceleration and start braking - // in order to reach the final_rate exactly at the end of this block. - if (plateau_steps < 0) { - accelerate_steps = ceilf(this->intersection_distance(this->initial_rate, this->final_rate, acceleration_per_second, this->steps_event_count)); - accelerate_steps = max( accelerate_steps, 0 ); // Check limits due to numerical round-off - accelerate_steps = min( accelerate_steps, int(this->steps_event_count) ); - plateau_steps = 0; + if (is_ticking) return; + + float initial_rate = this->nominal_rate * (entryspeed / this->nominal_speed); // steps/sec + float final_rate = this->nominal_rate * (exitspeed / this->nominal_speed); + //printf("Initial rate: %f, final_rate: %f\n", initial_rate, final_rate); + // How many steps ( can be fractions of steps, we need very precise values ) to accelerate and decelerate + // This is a simplification to get rid of rate_delta and get the steps/s² accel directly from the mm/s² accel + float acceleration_per_second = (this->acceleration * this->steps_event_count) / this->millimeters; + + float maximum_possible_rate = sqrtf( ( this->steps_event_count * acceleration_per_second ) + ( ( powf(initial_rate, 2) + powf(final_rate, 2) ) / 2.0F ) ); + + //printf("id %d: acceleration_per_second: %f, maximum_possible_rate: %f steps/sec, %f mm/sec\n", this->id, acceleration_per_second, maximum_possible_rate, maximum_possible_rate/100); + + // Now this is the maximum rate we'll achieve this move, either because + // it's the higher we can achieve, or because it's the higher we are + // allowed to achieve + this->maximum_rate = std::min(maximum_possible_rate, this->nominal_rate); + + // Now figure out how long it takes to accelerate in seconds + float time_to_accelerate = ( this->maximum_rate - initial_rate ) / acceleration_per_second; + + // Now figure out how long it takes to decelerate + float time_to_decelerate = ( final_rate - this->maximum_rate ) / -acceleration_per_second; + + // Now we know how long it takes to accelerate and decelerate, but we must + // also know how long the entire move takes so we can figure out how long + // is the plateau if there is one + float plateau_time = 0; + + // Only if there is actually a plateau ( we are limited by nominal_rate ) + if(maximum_possible_rate > this->nominal_rate) { + // Figure out the acceleration and deceleration distances ( in steps ) + float acceleration_distance = ( ( initial_rate + this->maximum_rate ) / 2.0F ) * time_to_accelerate; + float deceleration_distance = ( ( this->maximum_rate + final_rate ) / 2.0F ) * time_to_decelerate; + + // Figure out the plateau steps + float plateau_distance = this->steps_event_count - acceleration_distance - deceleration_distance; + + // Figure out the plateau time in seconds + plateau_time = plateau_distance / this->maximum_rate; } - this->accelerate_until = accelerate_steps; - this->decelerate_after = accelerate_steps + plateau_steps; - this->exit_speed = exitspeed; -} + // Figure out how long the move takes total ( in seconds ) + float total_move_time = time_to_accelerate + time_to_decelerate + plateau_time; + //puts "total move time: #{total_move_time}s time to accelerate: #{time_to_accelerate}, time to decelerate: #{time_to_decelerate}" -// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the -// given acceleration: -float Block::estimate_acceleration_distance(float initialrate, float targetrate, float acceleration) -{ - return( ((targetrate * targetrate) - (initialrate * initialrate)) / (2.0F * acceleration)); -} + // We now have the full timing for acceleration, plateau and deceleration, + // yay \o/ Now this is very important these are in seconds, and we need to + // round them into ticks. This means instead of accelerating in 100.23 + // ticks we'll accelerate in 100 ticks. Which means to reach the exact + // speed we want to reach, we must figure out a new/slightly different + // acceleration/deceleration to be sure we accelerate and decelerate at + // the exact rate we want -// This function gives you the point at which you must start braking (at the rate of -acceleration) if -// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after -// a total travel of distance. This can be used to compute the intersection point between acceleration and -// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) -// -/* + <- some maximum rate we don't care about - /|\ - / | \ - / | + <- final_rate - / | | - initial_rate -> +----+--+ - ^ ^ - | | - intersection_distance distance */ -float Block::intersection_distance(float initialrate, float finalrate, float acceleration, float distance) -{ - return((2 * acceleration * distance - initialrate * initialrate + finalrate * finalrate) / (4 * acceleration)); + // First off round total time, acceleration time and deceleration time in ticks + uint32_t acceleration_ticks = floorf( time_to_accelerate * STEP_TICKER_FREQUENCY ); + uint32_t deceleration_ticks = floorf( time_to_decelerate * STEP_TICKER_FREQUENCY ); + uint32_t total_move_ticks = floorf( total_move_time * STEP_TICKER_FREQUENCY ); + + // Now deduce the plateau time for those new values expressed in tick + //uint32_t plateau_ticks = total_move_ticks - acceleration_ticks - deceleration_ticks; + + // Now we figure out the acceleration value to reach EXACTLY maximum_rate(steps/s) in EXACTLY acceleration_ticks(ticks) amount of time in seconds + float acceleration_time = acceleration_ticks / STEP_TICKER_FREQUENCY; // This can be moved into the operation below, separated for clarity, note we need to do this instead of using time_to_accelerate(seconds) directly because time_to_accelerate(seconds) and acceleration_ticks(seconds) do not have the same value anymore due to the rounding + float deceleration_time = deceleration_ticks / STEP_TICKER_FREQUENCY; + + float acceleration_in_steps = (acceleration_time > 0.0F ) ? ( this->maximum_rate - initial_rate ) / acceleration_time : 0; + float deceleration_in_steps = (deceleration_time > 0.0F ) ? ( this->maximum_rate - final_rate ) / deceleration_time : 0; + + // we have a potential race condition here as we could get interrupted anywhere in the middle of this call, we need to lock + // the updates to the blocks to get around it + this->locked= true; + // Now figure out the two acceleration ramp change events in ticks + this->accelerate_until = acceleration_ticks; + this->decelerate_after = total_move_ticks - deceleration_ticks; + + // Now figure out the acceleration PER TICK, this should ideally be held as a float, even a double if possible as it's very critical to the block timing + // steps/tick^2 + + this->acceleration_per_tick = acceleration_in_steps / STEP_TICKER_FREQUENCY_2; + this->deceleration_per_tick = deceleration_in_steps / STEP_TICKER_FREQUENCY_2; + + // We now have everything we need for this block to call a Steppermotor->move method !!!! + // Theorically, if accel is done per tick, the speed curve should be perfect. + this->total_move_ticks = total_move_ticks; + + //puts "accelerate_until: #{this->accelerate_until}, decelerate_after: #{this->decelerate_after}, acceleration_per_tick: #{this->acceleration_per_tick}, total_move_ticks: #{this->total_move_ticks}" + + this->initial_rate = initial_rate; + this->exit_speed = exitspeed; + + // prepare the block for stepticker + this->prepare(); + this->locked= false; } // Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the @@ -162,7 +225,6 @@ float Block::max_allowable_speed(float acceleration, float target_velocity, floa return sqrtf(target_velocity * target_velocity - 2.0F * acceleration * distance); } - // Called by Planner::recalculate() when scanning the plan from last to first entry. float Block::reverse_pass(float exit_speed) { @@ -218,8 +280,8 @@ float Block::max_exit_speed() { // if block is currently executing, return cached exit speed from calculate_trapezoid // this ensures that a block following a currently executing block will have correct entry speed - if (times_taken) - return exit_speed; + if(is_ticking) + return this->exit_speed; // if nominal_length_flag is asserted // we are guaranteed to reach nominal speed regardless of entry speed @@ -233,59 +295,47 @@ float Block::max_exit_speed() return min(max, nominal_speed); } -// Gcodes are attached to their respective blocks so that on_gcode_execute can be called with it -void Block::append_gcode(Gcode* gcode) -{ - Gcode new_gcode = *gcode; - new_gcode.strip_parameters(); // optimization to save memory we strip off the XYZIJK parameters from the saved command - gcodes.push_back(new_gcode); -} - -void Block::begin() -{ - recalculate_flag = false; - - if (!is_ready) - __debugbreak(); - - times_taken = -1; - - // execute all the gcodes related to this block - for(unsigned int index = 0; index < gcodes.size(); index++) - THEKERNEL->call_event(ON_GCODE_EXECUTE, &(gcodes[index])); - - - THEKERNEL->call_event(ON_BLOCK_BEGIN, this); - - if (times_taken < 0) - release(); -} - -// Signal the conveyor that this block is ready to be injected into the system -void Block::ready() +// prepare block for the step ticker, called everytime the block changes +// this is done during planning so does not delay tick generation and step ticker can simply grab the next block during the interrupt +void Block::prepare() { - this->is_ready = true; -} + float inv = 1.0F / this->steps_event_count; + for (uint8_t m = 0; m < n_actuators; m++) { + uint32_t steps = this->steps[m]; + this->tick_info[m].steps_to_move = steps; + if(steps == 0) continue; + + float aratio = inv * steps; + this->tick_info[m].steps_per_tick = STEPTICKER_TOFP((this->initial_rate * aratio) / STEP_TICKER_FREQUENCY); // steps/sec / tick frequency to get steps per tick in 2.30 fixed point + this->tick_info[m].counter = 0; // 2.30 fixed point + this->tick_info[m].step_count = 0; + this->tick_info[m].next_accel_event = this->total_move_ticks + 1; + + float acceleration_change = 0; + if(this->accelerate_until != 0) { // If the next accel event is the end of accel + this->tick_info[m].next_accel_event = this->accelerate_until; + acceleration_change = this->acceleration_per_tick; + + } else if(this->decelerate_after == 0 /*&& this->accelerate_until == 0*/) { + // we start off decelerating + acceleration_change = -this->deceleration_per_tick; + + } else if(this->decelerate_after != this->total_move_ticks /*&& this->accelerate_until == 0*/) { + // If the next event is the start of decel ( don't set this if the next accel event is accel end ) + this->tick_info[m].next_accel_event = this->decelerate_after; + } -// Mark the block as taken by one more module -void Block::take() -{ - if (times_taken < 0) - times_taken = 0; - times_taken++; + // convert to fixed point after scaling + this->tick_info[m].acceleration_change= STEPTICKER_TOFP(acceleration_change * aratio); + this->tick_info[m].deceleration_change= -STEPTICKER_TOFP(this->deceleration_per_tick * aratio); + this->tick_info[m].plateau_rate= STEPTICKER_TOFP((this->maximum_rate * aratio) / STEP_TICKER_FREQUENCY); + } } -// Mark the block as no longer taken by one module, go to next block if this frees it -void Block::release() +// returns current rate (steps/sec) for the given actuator +float Block::get_trapezoid_rate(int i) const { - if (--this->times_taken <= 0) { - times_taken = 0; - if (is_ready) { - is_ready = false; - THEKERNEL->call_event(ON_BLOCK_END, this); - - // ensure conveyor gets called last - THEKERNEL->conveyor->on_block_end(this); - } - } + // convert steps per tick from fixed point to float and convert to steps/sec + // FIXME steps_per_tick can change at any time, potential race condition if it changes while being read here + return STEPTICKER_FROMFP(tick_info[i].steps_per_tick) * STEP_TICKER_FREQUENCY; } diff --git a/src/modules/robot/Block.h b/src/modules/robot/Block.h index 6f5b4feb..3df796ce 100644 --- a/src/modules/robot/Block.h +++ b/src/modules/robot/Block.h @@ -5,68 +5,75 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef BLOCK_H -#define BLOCK_H +#pragma once #include #include #include "ActuatorCoordinates.h" -class Gcode; - class Block { public: Block(); void calculate_trapezoid( float entry_speed, float exit_speed ); - float estimate_acceleration_distance( float initial_rate, float target_rate, float acceleration ); - float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance); float max_allowable_speed( float acceleration, float target_velocity, float distance); float reverse_pass(float exit_speed); float forward_pass(float next_entry_speed); - float max_exit_speed(); - - void debug(); - - void append_gcode(Gcode* gcode); - - void take(); - void release(); - - void ready(); - + void debug() const; + void ready() { is_ready= true; } void clear(); + void prepare(); - void begin(); - - std::vector gcodes; + float get_trapezoid_rate(int i) const; std::array steps; // Number of steps for each axis for this block uint32_t steps_event_count; // Steps for the longest axis - uint32_t nominal_rate; // Nominal rate in steps per second + float nominal_rate; // Nominal rate in steps per second float nominal_speed; // Nominal speed in mm per second float millimeters; // Distance for this move float entry_speed; float exit_speed; - float rate_delta; // Number of steps to add to the speed for each acceleration tick - float acceleration; // the acceleratoin for this block - uint32_t initial_rate; // Initial speed in steps per second - uint32_t final_rate; // Final speed in steps per second - uint32_t accelerate_until; // Stop accelerating after this number of steps - uint32_t decelerate_after; // Start decelerating after this number of steps + float acceleration; // the acceleration for this block + float initial_rate; // Initial rate in steps per second + float maximum_rate; - float max_entry_speed; + float acceleration_per_tick{0}; + float deceleration_per_tick {0}; - int16_t times_taken; // A block can be "taken" by any number of modules, and the next block is not moved to until all the modules have "released" it. This value serves as a tracker. + float max_entry_speed; + // this is tick info needed for this block. applies to all motors + uint32_t accelerate_until; + uint32_t decelerate_after; + uint32_t total_move_ticks; std::bitset direction_bits; // Direction for each axis in bit form, relative to the direction port's mask + + // this is the data needed to determine when each motor needs to be issued a step + using tickinfo_t= struct { + int32_t steps_per_tick; // 2.30 fixed point + int32_t counter; // 2.30 fixed point + int32_t acceleration_change; // 2.30 fixed point signed + int32_t deceleration_change; // 2.30 fixed point + int32_t plateau_rate; // 2.30 fixed point + uint32_t steps_to_move; + uint32_t step_count; + uint32_t next_accel_event; + }; + + // need info for each active motor + //std::array tick_info; + std::vector tick_info; + static uint8_t n_actuators; + struct { bool recalculate_flag:1; // Planner flag to recalculate trapezoids on entry junction bool nominal_length_flag:1; // Planner flag for nominal speed always reached bool is_ready:1; + bool primary_axis:1; // set if this move is a primary axis + bool is_g123:1; // set if this is a G1, G2 or G3 + volatile bool is_ticking:1; // set when this block is being actively ticked by the stepticker + volatile bool locked:1; // set to true when the critical data is being updated, stepticker will have to skip if this is set + uint16_t s_value:12; // for laser 1.11 Fixed point }; }; - - -#endif diff --git a/src/modules/robot/Conveyor.cpp b/src/modules/robot/Conveyor.cpp index b4f81e90..2d0ce2e5 100644 --- a/src/modules/robot/Conveyor.cpp +++ b/src/modules/robot/Conveyor.cpp @@ -5,8 +5,6 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -using namespace std; -#include #include "libs/nuts_bolts.h" #include "libs/RingBuffer.h" #include "../communication/utils/Gcode.h" @@ -22,8 +20,17 @@ using namespace std; #include "Config.h" #include "libs/StreamOutputPool.h" #include "ConfigValue.h" +#include "StepTicker.h" +#include "Robot.h" +#include "StepperMotor.h" + +#include +#include + +#include "mbed.h" #define planner_queue_size_checksum CHECKSUM("planner_queue_size") +#define queue_delay_time_ms_checksum CHECKSUM("queue_delay_time_ms") /* * The conveyor holds the queue of blocks, takes care of creating them, and starting the executing chain of blocks @@ -41,140 +48,110 @@ using namespace std; * * Both of these are implemented here- see queue_head_block() (where head is pushed) and on_idle() (where tail is consumed) * - * The double ring is implemented by adding a third index pointer that lives in between head and tail. We call it gc_pending which describes its function rather than its operation + * The double ring is implemented by adding a third index pointer that lives in between head and tail. We call it isr_tail_i. * - * in ISR context, we use HEAD as the head pointer, and gc_pending as the tail pointer. - * As HEAD increments, ISR context can consume the new blocks which appear, and when we're finished with a block, we increment gc_pending to signal that they're finishd, and ready to be cleaned + * in ISR context, we use HEAD as the head pointer, and isr_tail_i as the tail pointer. + * As HEAD increments, ISR context can consume the new blocks which appear, and when we're finished with a block, we increment isr_tail_i to signal that they're finished, and ready to be cleaned * - * in IDLE context, we use gc_pending as the head pointer, and TAIL as the tail pointer. - * When gc_pending != tail, we clean up the tail block (performing ISR-unsafe delete operations) and consume it (increment tail pointer), returning it to the pool of clean, unused blocks which HEAD is allowed to prepare for queueing + * in IDLE context, we use isr_tail_i as the head pointer, and TAIL as the tail pointer. + * When isr_tail_i != tail, we clean up the tail block (performing ISR-unsafe delete operations) and consume it (increment tail pointer), returning it to the pool of clean, unused blocks which HEAD is allowed to prepare for queueing * * Thus, our two ringbuffers exist sharing the one ring of blocks, and we safely marshall used blocks from ISR context to IDLE context for safe cleanup. */ -Conveyor::Conveyor(){ - gc_pending = queue.tail_i; + +Conveyor::Conveyor() +{ running = false; - flush = false; - halted= false; + halted = false; + allow_fetch = false; + flush= false; } -void Conveyor::on_module_loaded(){ +void Conveyor::on_module_loaded() +{ register_for_event(ON_IDLE); - register_for_event(ON_MAIN_LOOP); register_for_event(ON_HALT); - on_config_reload(this); + // Attach to the end_of_move stepper event + //THEKERNEL->step_ticker->finished_fnc = std::bind( &Conveyor::all_moves_finished, this); + queue_size = THEKERNEL->config->value(planner_queue_size_checksum)->by_default(32)->as_number(); + queue_delay_time_ms = THEKERNEL->config->value(queue_delay_time_ms_checksum)->by_default(100)->as_number(); +} + +// we allocate the queue here after config is completed so we do not run out of memory during config +void Conveyor::start(uint8_t n) +{ + Block::n_actuators= n; // set the number of motors which determines how big the tick info vector is + queue.resize(queue_size); + running = true; } -void Conveyor::on_halt(void* argument){ +void Conveyor::on_halt(void* argument) +{ if(argument == nullptr) { - halted= true; + halted = true; flush_queue(); - }else{ - halted= false; + } else { + halted = false; } } -// Delete blocks here, because they can't be deleted in interrupt context ( see Block.cpp:release ) -// note that blocks get cleaned as they come off the tail, so head ALWAYS points to a cleaned block. -void Conveyor::on_idle(void* argument){ - if (queue.tail_i != gc_pending) - { +void Conveyor::on_idle(void*) +{ + if (running) { + check_queue(); + } + + // we can garbage collect the block queue here + if (queue.tail_i != queue.isr_tail_i) { if (queue.is_empty()) { __debugbreak(); - }else{ + } else { // Cleanly delete block Block* block = queue.tail_ref(); -// block->debug(); + //block->debug(); block->clear(); queue.consume_tail(); } } } -/* - * In on_main_loop, we check whether the queue should be running, but isn't. - * - * The main trigger for this event is other pieces of code adding gcode to a block, but not pushing it. This occurs frequently with gcodes that must be executed at the correct point in the queue, but take zero time to execute. - * Smoothie will happily attach many of such gcodes onto a single block, to save room in the queue. - * - * Any gcode which can potentially take time to execute, or might like to halt the queue MUST push the head block, otherwise gcodes that arrive later may get executed at the same time, and gcode execution order strictness would be violated. - * - * If we get back to main loop context and the block has gcode but isn't pushed, then we can safely push it and start the queue. - * - * - * It's also theoretically possible that a race condition could occur where we pop the final block and stop the queue, while at the same time main loop is pushing head but thinks the queue is running and thus does not start it. - * - * In this case, we start the queue again when execution returns to main loop. - * No stuttering or other visible effects could be caused by this event, as the planner will have set the last block to decelerate to zero, and the new block to accelerate from zero. - * - */ - -void Conveyor::on_main_loop(void*) +// see if we are idle +// this checks the block queue is empty, and that the step queue is empty and +// checks that all motors are no longer moving +bool Conveyor::is_idle() const { - if (running) - return; - - if (queue.is_empty()) - { - if (queue.head_ref()->gcodes.size()) - { - queue_head_block(); - ensure_running(); + if(queue.is_empty()) { + for(auto &a : THEROBOT->actuators) { + if(a->is_moving()) return false; } + return true; } - else - // queue not empty - ensure_running(); -} -void Conveyor::on_config_reload(void* argument) -{ - queue.resize(THEKERNEL->config->value(planner_queue_size_checksum)->by_default(32)->as_number()); + return false; } -void Conveyor::append_gcode(Gcode* gcode) +// Wait for the queue to be empty and for all the jobs to finish in step ticker +void Conveyor::wait_for_idle(bool wait_for_motors) { - queue.head_ref()->append_gcode(gcode); -} - -// Process a new block in the queue -void Conveyor::on_block_end(void* block) -{ - if (queue.is_empty()) - __debugbreak(); - - gc_pending = queue.next(gc_pending); - - // mark entire queue for GC if flush flag is asserted - if (flush){ - while (gc_pending != queue.head_i) { - gc_pending = queue.next(gc_pending); - } + // wait for the job queue to empty, this means cycling everything on the block queue into the job queue + // forcing them to be jobs + running = false; // stops on_idle calling check_queue + while (!queue.is_empty()) { + check_queue(true); // forces queue to be made available to stepticker + THEKERNEL->call_event(ON_IDLE, this); } - // Return if queue is empty - if (gc_pending == queue.head_i) - { - running = false; - return; + if(wait_for_motors) { + // now we wait for all motors to stop moving + while(!is_idle()) { + THEKERNEL->call_event(ON_IDLE, this); + } } - // Get a new block - Block* next = this->queue.item_ref(gc_pending); - current_feedrate= next->nominal_speed; - - next->begin(); -} - -// Wait for the queue to be empty -void Conveyor::wait_for_empty_queue() -{ - while (!queue.is_empty()) { - ensure_running(); - THEKERNEL->call_event(ON_IDLE, this); - } + running = true; + // returning now means that everything has totally finished } /* @@ -183,56 +160,107 @@ void Conveyor::wait_for_empty_queue() void Conveyor::queue_head_block() { // upstream caller will block on this until there is room in the queue - while (queue.is_full()) { - ensure_running(); - THEKERNEL->call_event(ON_IDLE, this); + while (queue.is_full() && !halted) { + //check_queue(); + THEKERNEL->call_event(ON_IDLE, this); // will call check_queue(); } if(halted) { // we do not want to stick more stuff on the queue if we are in halt state // clear and release the block on the head queue.head_ref()->clear(); + return; // if we got a halt then we are done here + } + + queue.produce_head(); + + // not sure if this is the correct place but we need to turn on the motors if they were not already on + THEKERNEL->call_event(ON_ENABLE, (void*)1); // turn all enable pins on +} + +void Conveyor::check_queue(bool force) +{ + static uint32_t last_time_check = us_ticker_read(); + + if(queue.is_empty()) { + allow_fetch = false; + last_time_check = us_ticker_read(); // reset timeout + return; + } - }else{ - queue.head_ref()->ready(); - queue.produce_head(); + // if we have been waiting for more than the required waiting time and the queue is not empty, or the queue is full, then allow stepticker to get the tail + // we do this to allow an idle system to pre load the queue a bit so the first few blocks run smoothly. + if(force || queue.is_full() || (us_ticker_read() - last_time_check) >= (queue_delay_time_ms * 1000)) { + last_time_check = us_ticker_read(); // reset timeout + if(!flush) allow_fetch = true; + return; } } -void Conveyor::ensure_running() +// called from step ticker ISR +bool Conveyor::get_next_block(Block **block) { - if (!running) - { - if (gc_pending == queue.head_i) - return; + // mark entire queue for GC if flush flag is asserted + if (flush){ + while (queue.isr_tail_i != queue.head_i) { + queue.isr_tail_i = queue.next(queue.isr_tail_i); + } + } - running = true; - queue.item_ref(gc_pending)->begin(); + // default the feerate to zero if there is no block available + this->current_feedrate= 0; + + if(halted || queue.isr_tail_i == queue.head_i) return false; // we do not have anything to give + + // wait for queue to fill up, optimizes planning + if(!allow_fetch) return false; + + Block *b= queue.item_ref(queue.isr_tail_i); + // we cannot use this now if it is being updated + if(!b->locked) { + if(!b->is_ready) __debugbreak(); // should never happen + + b->is_ticking= true; + b->recalculate_flag= false; + this->current_feedrate= b->nominal_speed; + *block= b; + return true; } + + return false; } -/* +// called from step ticker ISR when block is finished, do not do anything slow here +void Conveyor::block_finished() +{ + // we increment the isr_tail_i so we can get the next block + queue.isr_tail_i= queue.next(queue.isr_tail_i); +} +/* In most cases this will not totally flush the queue, as when streaming gcode there is one stalled waiting for space in the queue, in queue_head_block() so after this flush, once main_loop runs again one more gcode gets stuck in the queue, this is bad. Current work around is to call this when the queue in not full and streaming has stopped - */ - void Conveyor::flush_queue() { - flush = true; - wait_for_empty_queue(); - flush = false; + allow_fetch = false; + flush= true; + + // TODO force deceleration of last block + + // now wait until the block queue has been flushed + wait_for_idle(false); + + flush= false; } // Debug function void Conveyor::dump_queue() { - for (unsigned int index = queue.tail_i, i = 0; true; index = queue.next(index), i++ ) - { + for (unsigned int index = queue.tail_i, i = 0; true; index = queue.next(index), i++ ) { THEKERNEL->streams->printf("block %03d > ", i); queue.item_ref(index)->debug(); diff --git a/src/modules/robot/Conveyor.h b/src/modules/robot/Conveyor.h index 936a7501..ba2b09a7 100644 --- a/src/modules/robot/Conveyor.h +++ b/src/modules/robot/Conveyor.h @@ -5,8 +5,7 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef CONVEYOR_H -#define CONVEYOR_H +#pragma once #include "libs/Module.h" #include "HeapRing.h" @@ -22,44 +21,45 @@ class Conveyor : public Module { public: Conveyor(); + void start(uint8_t n_actuators); void on_module_loaded(void); void on_idle(void *); - void on_main_loop(void *); - void on_block_end(void *); void on_halt(void *); - void on_config_reload(void *); - void notify_block_finished(Block *); - - void wait_for_empty_queue(); + void wait_for_idle(bool wait_for_motors=true); bool is_queue_empty() { return queue.is_empty(); }; bool is_queue_full() { return queue.is_full(); }; + bool is_idle() const; - void ensure_running(void); - - void append_gcode(Gcode *); - void queue_head_block(void); + // returns next available block writes it to block and returns true + bool get_next_block(Block **block); + void block_finished(); void dump_queue(void); void flush_queue(void); - bool is_flushing() const { return flush; } float get_current_feedrate() const { return current_feedrate; } + friend class Planner; // for queue private: - typedef HeapRing Queue_t; + // void all_moves_finished(); + void check_queue(bool force= false); + void queue_head_block(void); + using Queue_t= HeapRing; Queue_t queue; // Queue of Blocks - volatile unsigned int gc_pending; + //volatile unsigned int gc_pending; + + uint32_t queue_delay_time_ms; + size_t queue_size; float current_feedrate{0}; // actual nominal feedrate that current block is running at in mm/sec struct { volatile bool running:1; - volatile bool flush:1; volatile bool halted:1; + volatile bool allow_fetch:1; + bool flush:1; }; }; - -#endif // CONVEYOR_H diff --git a/src/modules/robot/Planner.cpp b/src/modules/robot/Planner.cpp index 371d6774..1432e44a 100644 --- a/src/modules/robot/Planner.cpp +++ b/src/modules/robot/Planner.cpp @@ -5,6 +5,7 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ +using namespace std; #include #include "mri.h" @@ -20,15 +21,11 @@ #include "Config.h" #include "checksumm.h" #include "Robot.h" -#include "Stepper.h" #include "ConfigValue.h" #include +#include -using namespace std; - -#define acceleration_checksum CHECKSUM("acceleration") -#define z_acceleration_checksum CHECKSUM("z_acceleration") #define junction_deviation_checksum CHECKSUM("junction_deviation") #define z_junction_deviation_checksum CHECKSUM("z_junction_deviation") #define minimum_planner_speed_checksum CHECKSUM("minimum_planner_speed") @@ -39,69 +36,78 @@ using namespace std; Planner::Planner() { - clear_vector_float(this->previous_unit_vec); + memset(this->previous_unit_vec, 0, sizeof this->previous_unit_vec); config_load(); } // Configure acceleration void Planner::config_load() { - this->acceleration = THEKERNEL->config->value(acceleration_checksum)->by_default(100.0F )->as_number(); // Acceleration is in mm/s^2 - this->z_acceleration = THEKERNEL->config->value(z_acceleration_checksum)->by_default(0.0F )->as_number(); // disabled by default - this->junction_deviation = THEKERNEL->config->value(junction_deviation_checksum)->by_default(0.05F)->as_number(); - this->z_junction_deviation = THEKERNEL->config->value(z_junction_deviation_checksum)->by_default(-1)->as_number(); // disabled by default + this->z_junction_deviation = THEKERNEL->config->value(z_junction_deviation_checksum)->by_default(NAN)->as_number(); // disabled by default this->minimum_planner_speed = THEKERNEL->config->value(minimum_planner_speed_checksum)->by_default(0.0f)->as_number(); } // Append a block to the queue, compute it's speed factors -void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, float distance, float unit_vec[] ) +bool Planner::append_block( ActuatorCoordinates &actuator_pos, uint8_t n_motors, float rate_mm_s, float distance, float *unit_vec, float acceleration, float s_value, bool g123) { - float acceleration, junction_deviation; - // Create ( recycle ) a new block - Block* block = THEKERNEL->conveyor->queue.head_ref(); - + Block* block = THECONVEYOR->queue.head_ref(); // Direction bits - for (size_t i = 0; i < THEKERNEL->robot->actuators.size(); i++) { - int steps = THEKERNEL->robot->actuators[i]->steps_to_target(actuator_pos[i]); - - block->direction_bits[i] = (steps < 0) ? 1 : 0; - + bool has_steps = false; + for (size_t i = 0; i < n_motors; i++) { + int32_t steps = THEROBOT->actuators[i]->steps_to_target(actuator_pos[i]); // Update current position - THEKERNEL->robot->actuators[i]->last_milestone_steps += steps; - THEKERNEL->robot->actuators[i]->last_milestone_mm = actuator_pos[i]; + if(steps != 0) { + THEROBOT->actuators[i]->update_last_milestones(actuator_pos[i], steps); + has_steps = true; + } + // find direction + block->direction_bits[i] = (steps < 0) ? 1 : 0; + // save actual steps in block block->steps[i] = labs(steps); } - acceleration = this->acceleration; - junction_deviation = this->junction_deviation; + // sometimes even though there is a detectable movement it turns out there are no steps to be had from such a small move + if(!has_steps) { + block->clear(); + return false; + } + + // info needed by laser + block->s_value = roundf(s_value*(1<<11)); // 1.11 fixed point + block->is_g123 = g123; - // use either regular acceleration or a z only move accleration + // use default JD + float junction_deviation = this->junction_deviation; + + // use either regular junction deviation or z specific and see if a primary axis move + block->primary_axis = true; if(block->steps[ALPHA_STEPPER] == 0 && block->steps[BETA_STEPPER] == 0) { - // z only move - if(this->z_acceleration > 0.0F) acceleration = this->z_acceleration; - if(this->z_junction_deviation >= 0.0F) junction_deviation = this->z_junction_deviation; + if(block->steps[GAMMA_STEPPER] != 0) { + // z only move + if(!isnan(this->z_junction_deviation)) junction_deviation = this->z_junction_deviation; + } else { + // is not a primary axis move + block->primary_axis = false; + } } block->acceleration = acceleration; // save in block // Max number of steps, for all axes - uint32_t steps_event_count = 0; - for (size_t s = 0; s < THEKERNEL->robot->actuators.size(); s++) { - steps_event_count = std::max(steps_event_count, block->steps[s]); - } - block->steps_event_count = steps_event_count; + auto mi = std::max_element(block->steps.begin(), block->steps.end()); + block->steps_event_count = *mi; block->millimeters = distance; // Calculate speed in mm/sec for each axis. No divide by zero due to previous checks. if( distance > 0.0F ) { block->nominal_speed = rate_mm_s; // (mm/s) Always > 0 - block->nominal_rate = ceilf(block->steps_event_count * rate_mm_s / distance); // (step/s) Always > 0 + block->nominal_rate = block->steps_event_count * rate_mm_s / distance; // (step/s) Always > 0 } else { block->nominal_speed = 0.0F; block->nominal_rate = 0; @@ -111,10 +117,6 @@ void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, // average travel per step event changes. For a line along one axis the travel per step event // is equal to the travel/step in the particular axis. For a 45 degree line the steppers of both // axes might step for every step event. Travel per step event is then sqrt(travel_x^2+travel_y^2). - // To generate trapezoids with contant acceleration between blocks the rate_delta must be computed - // specifically for each line to compensate for this phenomenon: - // Convert universal acceleration for direction-dependent stepper rate change parameter - block->rate_delta = (block->steps_event_count * acceleration) / (distance * THEKERNEL->acceleration_ticks_per_second); // (step/min/acceleration_tick) // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. // Let a circle be tangent to both previous and current path line segments, where the junction @@ -130,10 +132,12 @@ void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, // and this allows one to stop with little to no decleration in many cases. This is particualrly bad on leadscrew based systems that will skip steps. float vmax_junction = minimum_planner_speed; // Set default max junction speed - if (!THEKERNEL->conveyor->is_queue_empty()) { - float previous_nominal_speed = THEKERNEL->conveyor->queue.item_ref(THEKERNEL->conveyor->queue.prev(THEKERNEL->conveyor->queue.head_i))->nominal_speed; + // if unit_vec was null then it was not a primary axis move so we skip the junction deviation stuff + if (unit_vec != nullptr && !THECONVEYOR->is_queue_empty()) { + Block *prev_block = THECONVEYOR->queue.item_ref(THECONVEYOR->queue.prev(THECONVEYOR->queue.head_i)); + float previous_nominal_speed = prev_block->primary_axis ? prev_block->nominal_speed : 0; - if (previous_nominal_speed > 0.0F && junction_deviation > 0.0F) { + if (junction_deviation > 0.0F && previous_nominal_speed > 0.0F) { // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. float cos_theta = - this->previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] @@ -142,12 +146,12 @@ void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, // Skip and use default max junction speed for 0 degree acute junction. if (cos_theta < 0.95F) { - vmax_junction = min(previous_nominal_speed, block->nominal_speed); + vmax_junction = std::min(previous_nominal_speed, block->nominal_speed); // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. if (cos_theta > -0.95F) { // Compute maximum junction velocity based on maximum acceleration and junction deviation float sin_theta_d2 = sqrtf(0.5F * (1.0F - cos_theta)); // Trig half angle identity. Always positive. - vmax_junction = min(vmax_junction, sqrtf(acceleration * junction_deviation * sin_theta_d2 / (1.0F - sin_theta_d2))); + vmax_junction = std::min(vmax_junction, sqrtf(acceleration * junction_deviation * sin_theta_d2 / (1.0F - sin_theta_d2))); } } } @@ -156,7 +160,7 @@ void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, // Initialize block entry speed. Compute based on deceleration to user-defined minimum_planner_speed. float v_allowable = max_allowable_speed(-acceleration, minimum_planner_speed, block->millimeters); - block->entry_speed = min(vmax_junction, v_allowable); + block->entry_speed = std::min(vmax_junction, v_allowable); // Initialize planner efficiency flags // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. @@ -173,7 +177,11 @@ void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, block->recalculate_flag = true; // Update previous path unit_vector and nominal speed - memcpy(this->previous_unit_vec, unit_vec, sizeof(previous_unit_vec)); // previous_unit_vec[] = unit_vec[] + if(unit_vec != nullptr) { + memcpy(previous_unit_vec, unit_vec, sizeof(previous_unit_vec)); // previous_unit_vec[] = unit_vec[] + } else { + memset(previous_unit_vec, 0, sizeof(previous_unit_vec)); + } // Math-heavy re-computing of the whole queue to take the new this->recalculate(); @@ -181,12 +189,14 @@ void Planner::append_block( ActuatorCoordinates &actuator_pos, float rate_mm_s, // The block can now be used block->ready(); - THEKERNEL->conveyor->queue_head_block(); + THECONVEYOR->queue_head_block(); + + return true; } void Planner::recalculate() { - Conveyor::Queue_t &queue = THEKERNEL->conveyor->queue; + Conveyor::Queue_t &queue = THECONVEYOR->queue; unsigned int block_index; @@ -241,7 +251,7 @@ void Planner::recalculate() * Step 2: * now current points to either tail or first non-recalculate block * and has not had its reverse_pass called - * or its calc trap + * or its calculate_trapezoid * entry_speed is set to the *exit* speed of current. * each block from current to head has its entry speed set to its max entry speed- limited by decel or nominal_rate */ @@ -276,9 +286,8 @@ void Planner::recalculate() // acceleration within the allotted distance. float Planner::max_allowable_speed(float acceleration, float target_velocity, float distance) { - return( - sqrtf(target_velocity * target_velocity - 2.0F * acceleration * distance) //Was acceleration*60*60*distance, in case this breaks, but here we prefer to use seconds instead of minutes - ); + // Was acceleration*60*60*distance, in case this breaks, but here we prefer to use seconds instead of minutes + return(sqrtf(target_velocity * target_velocity - 2.0F * acceleration * distance)); } diff --git a/src/modules/robot/Planner.h b/src/modules/robot/Planner.h index 79648b59..2d10784f 100644 --- a/src/modules/robot/Planner.h +++ b/src/modules/robot/Planner.h @@ -15,21 +15,15 @@ class Planner { public: Planner(); - void append_block(ActuatorCoordinates &target, float rate_mm_s, float distance, float unit_vec[] ); float max_allowable_speed( float acceleration, float target_velocity, float distance); - void recalculate(); - Block *get_current_block(); - void cleanup_queue(); - float get_acceleration() const { return acceleration; } - float get_z_acceleration() const { return z_acceleration > 0.0F ? z_acceleration : acceleration; } friend class Robot; // for acceleration, junction deviation, minimum_planner_speed private: + bool append_block(ActuatorCoordinates &target, uint8_t n_motors, float rate_mm_s, float distance, float unit_vec[], float accleration, float s_value, bool g123); + void recalculate(); void config_load(); float previous_unit_vec[3]; - float acceleration; // Setting - float z_acceleration; // Setting float junction_deviation; // Setting float z_junction_deviation; // Setting float minimum_planner_speed; // Setting diff --git a/src/modules/robot/Robot.cpp b/src/modules/robot/Robot.cpp index d09f0d0d..e9512ba1 100644 --- a/src/modules/robot/Robot.cpp +++ b/src/modules/robot/Robot.cpp @@ -8,16 +8,9 @@ #include "libs/Module.h" #include "libs/Kernel.h" -#include "mbed.h" // for us_ticker_read() - -#include -#include -using std::string; - +#include "Robot.h" #include "Planner.h" #include "Conveyor.h" -#include "Robot.h" -#include "nuts_bolts.h" #include "Pin.h" #include "StepperMotor.h" #include "Gcode.h" @@ -39,19 +32,29 @@ using std::string; #include "StreamOutputPool.h" #include "ExtruderPublicAccess.h" #include "GcodeDispatch.h" +#include "ActuatorCoordinates.h" +#include "mbed.h" // for us_ticker_read() +#include "mri.h" + +#include +#include +#include +using std::string; #define default_seek_rate_checksum CHECKSUM("default_seek_rate") #define default_feed_rate_checksum CHECKSUM("default_feed_rate") #define mm_per_line_segment_checksum CHECKSUM("mm_per_line_segment") #define delta_segments_per_second_checksum CHECKSUM("delta_segments_per_second") #define mm_per_arc_segment_checksum CHECKSUM("mm_per_arc_segment") +#define mm_max_arc_error_checksum CHECKSUM("mm_max_arc_error") #define arc_correction_checksum CHECKSUM("arc_correction") #define x_axis_max_speed_checksum CHECKSUM("x_axis_max_speed") #define y_axis_max_speed_checksum CHECKSUM("y_axis_max_speed") #define z_axis_max_speed_checksum CHECKSUM("z_axis_max_speed") #define segment_z_moves_checksum CHECKSUM("segment_z_moves") #define save_g92_checksum CHECKSUM("save_g92") +#define set_g92_checksum CHECKSUM("set_g92") // arm solutions #define arm_solution_checksum CHECKSUM("arm_solution") @@ -76,33 +79,17 @@ using std::string; #define steps_per_mm_checksum CHECKSUM("steps_per_mm") #define max_rate_checksum CHECKSUM("max_rate") +#define acceleration_checksum CHECKSUM("acceleration") +#define z_acceleration_checksum CHECKSUM("z_acceleration") #define alpha_checksum CHECKSUM("alpha") #define beta_checksum CHECKSUM("beta") #define gamma_checksum CHECKSUM("gamma") -#define NEXT_ACTION_DEFAULT 0 -#define NEXT_ACTION_DWELL 1 -#define NEXT_ACTION_GO_HOME 2 - -#define MOTION_MODE_SEEK 0 // G0 -#define MOTION_MODE_LINEAR 1 // G1 -#define MOTION_MODE_CW_ARC 2 // G2 -#define MOTION_MODE_CCW_ARC 3 // G3 -#define MOTION_MODE_CANCEL 4 // G80 - -#define PATH_CONTROL_MODE_EXACT_PATH 0 -#define PATH_CONTROL_MODE_EXACT_STOP 1 -#define PATH_CONTROL_MODE_CONTINOUS 2 - -#define PROGRAM_FLOW_RUNNING 0 -#define PROGRAM_FLOW_PAUSED 1 -#define PROGRAM_FLOW_COMPLETED 2 +#define laser_module_default_power_checksum CHECKSUM("laser_module_default_power") -#define SPINDLE_DIRECTION_CW 0 -#define SPINDLE_DIRECTION_CCW 1 - -#define ARC_ANGULAR_TRAVEL_EPSILON 5E-7 // Float (radians) +#define ARC_ANGULAR_TRAVEL_EPSILON 5E-7F // Float (radians) +#define PI 3.14159265358979323846F // force to be float, do not use M_PI // The Robot converts GCodes into actual movements, and then adds them to the Planner, which passes them to the Conveyor so they can be added to the queue // It takes care of cutting arcs into segments, same thing for line that are too long @@ -111,18 +98,21 @@ Robot::Robot() { this->inch_mode = false; this->absolute_mode = true; - this->motion_mode = MOTION_MODE_SEEK; + this->e_absolute_mode = true; this->select_plane(X_AXIS, Y_AXIS, Z_AXIS); - clear_vector(this->last_milestone); - clear_vector(this->last_machine_position); + memset(this->last_milestone, 0, sizeof last_milestone); + memset(this->last_machine_position, 0, sizeof last_machine_position); this->arm_solution = NULL; seconds_per_minute = 60.0F; this->clearToolOffset(); this->compensationTransform = nullptr; + this->get_e_scale_fnc= nullptr; this->wcs_offsets.fill(wcs_t(0.0F, 0.0F, 0.0F)); this->g92_offset = wcs_t(0.0F, 0.0F, 0.0F); this->next_command_is_MCS = false; this->disable_segmentation= false; + this->disable_arm_solution= false; + this->n_motors= 0; } //Called when the module has just been loaded @@ -139,7 +129,8 @@ void Robot::on_module_loaded() CHECKSUM(X "_dir_pin"), \ CHECKSUM(X "_en_pin"), \ CHECKSUM(X "_steps_per_mm"), \ - CHECKSUM(X "_max_rate") \ + CHECKSUM(X "_max_rate"), \ + CHECKSUM(X "_acceleration") \ } void Robot::load_config() @@ -180,59 +171,99 @@ void Robot::load_config() this->seek_rate = THEKERNEL->config->value(default_seek_rate_checksum )->by_default( 100.0F)->as_number(); this->mm_per_line_segment = THEKERNEL->config->value(mm_per_line_segment_checksum )->by_default( 0.0F)->as_number(); this->delta_segments_per_second = THEKERNEL->config->value(delta_segments_per_second_checksum )->by_default(0.0f )->as_number(); - this->mm_per_arc_segment = THEKERNEL->config->value(mm_per_arc_segment_checksum )->by_default( 0.5f)->as_number(); + this->mm_per_arc_segment = THEKERNEL->config->value(mm_per_arc_segment_checksum )->by_default( 0.0f)->as_number(); + this->mm_max_arc_error = THEKERNEL->config->value(mm_max_arc_error_checksum )->by_default( 0.01f)->as_number(); this->arc_correction = THEKERNEL->config->value(arc_correction_checksum )->by_default( 5 )->as_number(); + // in mm/sec but specified in config as mm/min this->max_speeds[X_AXIS] = THEKERNEL->config->value(x_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; this->max_speeds[Y_AXIS] = THEKERNEL->config->value(y_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; this->max_speeds[Z_AXIS] = THEKERNEL->config->value(z_axis_max_speed_checksum )->by_default( 300.0F)->as_number() / 60.0F; this->segment_z_moves = THEKERNEL->config->value(segment_z_moves_checksum )->by_default(true)->as_bool(); this->save_g92 = THEKERNEL->config->value(save_g92_checksum )->by_default(false)->as_bool(); + string g92 = THEKERNEL->config->value(set_g92_checksum )->by_default("")->as_string(); + if(!g92.empty()) { + // optional setting for a fixed G92 offset + std::vector t= parse_number_list(g92.c_str()); + if(t.size() == 3) { + g92_offset = wcs_t(t[0], t[1], t[2]); + } + } - // Make our 3 StepperMotors - uint16_t const checksums[][5] = { - ACTUATOR_CHECKSUMS("alpha"), - ACTUATOR_CHECKSUMS("beta"), - ACTUATOR_CHECKSUMS("gamma"), -#if MAX_ROBOT_ACTUATORS > 3 - ACTUATOR_CHECKSUMS("delta"), - ACTUATOR_CHECKSUMS("epsilon"), - ACTUATOR_CHECKSUMS("zeta") -#endif + // default s value for laser + this->s_value = THEKERNEL->config->value(laser_module_default_power_checksum)->by_default(0.8F)->as_number(); + + // Make our Primary XYZ StepperMotors + uint16_t const checksums[][6] = { + ACTUATOR_CHECKSUMS("alpha"), // X + ACTUATOR_CHECKSUMS("beta"), // Y + ACTUATOR_CHECKSUMS("gamma"), // Z }; - constexpr size_t actuator_checksum_count = sizeof(checksums) / sizeof(checksums[0]); - static_assert(actuator_checksum_count >= k_max_actuators, "Robot checksum array too small for k_max_actuators"); - size_t motor_count = std::min(this->arm_solution->get_actuator_count(), k_max_actuators); - for (size_t a = 0; a < motor_count; a++) { + // default acceleration setting, can be overriden with newer per axis settings + this->default_acceleration= THEKERNEL->config->value(acceleration_checksum)->by_default(100.0F )->as_number(); // Acceleration is in mm/s^2 + + // make each motor + for (size_t a = X_AXIS; a <= Z_AXIS; a++) { Pin pins[3]; //step, dir, enable for (size_t i = 0; i < 3; i++) { pins[i].from_string(THEKERNEL->config->value(checksums[a][i])->by_default("nc")->as_string())->as_output(); } - actuators[a] = new StepperMotor(pins[0], pins[1], pins[2]); + StepperMotor *sm = new StepperMotor(pins[0], pins[1], pins[2]); + // register this motor (NB This must be 0,1,2) of the actuators array + uint8_t n= register_motor(sm); + if(n != a) { + // this is a fatal error + THEKERNEL->streams->printf("FATAL: motor %d does not match index %d\n", n, a); + __debugbreak(); + } actuators[a]->change_steps_per_mm(THEKERNEL->config->value(checksums[a][3])->by_default(a == 2 ? 2560.0F : 80.0F)->as_number()); - actuators[a]->set_max_rate(THEKERNEL->config->value(checksums[a][4])->by_default(30000.0F)->as_number()/60.0F); + actuators[a]->set_max_rate(THEKERNEL->config->value(checksums[a][4])->by_default(30000.0F)->as_number()/60.0F); // it is in mm/min and converted to mm/sec + actuators[a]->set_acceleration(THEKERNEL->config->value(checksums[a][5])->by_default(NAN)->as_number()); // mm/secs² } check_max_actuator_speeds(); // check the configs are sane + // if we have not specified a z acceleration see if the legacy config was set + if(isnan(actuators[Z_AXIS]->get_acceleration())) { + float acc= THEKERNEL->config->value(z_acceleration_checksum)->by_default(NAN)->as_number(); // disabled by default + if(!isnan(acc)) { + actuators[Z_AXIS]->set_acceleration(acc); + } + } + // initialise actuator positions to current cartesian position (X0 Y0 Z0) // so the first move can be correct if homing is not performed ActuatorCoordinates actuator_pos; arm_solution->cartesian_to_actuator(last_milestone, actuator_pos); - for (size_t i = 0; i < actuators.size(); i++) + for (size_t i = 0; i < n_motors; i++) actuators[i]->change_last_milestone(actuator_pos[i]); //this->clearToolOffset(); } +uint8_t Robot::register_motor(StepperMotor *motor) +{ + // register this motor with the step ticker + THEKERNEL->step_ticker->register_motor(motor); + if(n_motors >= k_max_actuators) { + // this is a fatal error + THEKERNEL->streams->printf("FATAL: too many motors, increase k_max_actuators\n"); + __debugbreak(); + } + actuators.push_back(motor); + motor->set_motor_id(n_motors); + return n_motors++; +} + void Robot::push_state() { bool am = this->absolute_mode; + bool em = this->e_absolute_mode; bool im = this->inch_mode; - saved_state_t s(this->feed_rate, this->seek_rate, am, im, current_wcs); + saved_state_t s(this->feed_rate, this->seek_rate, am, em, im, current_wcs); state_stack.push(s); } @@ -244,8 +275,9 @@ void Robot::pop_state() this->feed_rate = std::get<0>(s); this->seek_rate = std::get<1>(s); this->absolute_mode = std::get<2>(s); - this->inch_mode = std::get<3>(s); - this->current_wcs = std::get<4>(s); + this->e_absolute_mode = std::get<3>(s); + this->inch_mode = std::get<4>(s); + this->current_wcs = std::get<5>(s); } } @@ -295,13 +327,13 @@ int Robot::print_position(uint8_t subcode, char *buf, size_t bufsize) const if(subcode == 1) { // M114.1 print realtime WCS // FIXME this currently includes the compensation transform which is incorrect so will be slightly off if it is in effect (but by very little) wcs_t pos= mcs2wcs(mpos); - n = snprintf(buf, bufsize, "C: X:%1.4f Y:%1.4f Z:%1.4f", from_millimeters(std::get(pos)), from_millimeters(std::get(pos)), from_millimeters(std::get(pos))); + n = snprintf(buf, bufsize, "WPOS: X:%1.4f Y:%1.4f Z:%1.4f", from_millimeters(std::get(pos)), from_millimeters(std::get(pos)), from_millimeters(std::get(pos))); } else if(subcode == 2) { // M114.2 print realtime Machine coordinate system n = snprintf(buf, bufsize, "MPOS: X:%1.4f Y:%1.4f Z:%1.4f", mpos[X_AXIS], mpos[Y_AXIS], mpos[Z_AXIS]); } else if(subcode == 3) { // M114.3 print realtime actuator position - n = snprintf(buf, bufsize, "APOS: A:%1.4f B:%1.4f C:%1.4f", current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]); + n = snprintf(buf, bufsize, "APOS: X:%1.4f Y:%1.4f Z:%1.4f", current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]); } } return n; @@ -321,11 +353,11 @@ Robot::wcs_t Robot::mcs2wcs(const Robot::wcs_t& pos) const // we will override the actuator max_rate if the combination of max_rate and steps/sec exceeds base_stepping_frequency void Robot::check_max_actuator_speeds() { - for (size_t i = 0; i < actuators.size(); i++) { + for (size_t i = 0; i < n_motors; i++) { float step_freq = actuators[i]->get_max_rate() * actuators[i]->get_steps_per_mm(); if (step_freq > THEKERNEL->base_stepping_frequency) { actuators[i]->set_max_rate(floorf(THEKERNEL->base_stepping_frequency / actuators[i]->get_steps_per_mm())); - THEKERNEL->streams->printf("WARNING: actuator %c rate exceeds base_stepping_frequency * alpha_steps_per_mm: %f, setting to %f\n", 'A' + i, step_freq, actuators[i]->max_rate); + THEKERNEL->streams->printf("WARNING: actuator %d rate exceeds base_stepping_frequency * ..._steps_per_mm: %f, setting to %f\n", i, step_freq, actuators[i]->get_max_rate()); } } } @@ -336,14 +368,14 @@ void Robot::on_gcode_received(void *argument) { Gcode *gcode = static_cast(argument); - this->motion_mode = -1; + enum MOTION_MODE_T motion_mode= NONE; if( gcode->has_g) { switch( gcode->g ) { - case 0: this->motion_mode = MOTION_MODE_SEEK; break; - case 1: this->motion_mode = MOTION_MODE_LINEAR; break; - case 2: this->motion_mode = MOTION_MODE_CW_ARC; break; - case 3: this->motion_mode = MOTION_MODE_CCW_ARC; break; + case 0: motion_mode = SEEK; break; + case 1: motion_mode = LINEAR; break; + case 2: motion_mode = CW_ARC; break; + case 3: motion_mode = CCW_ARC; break; case 4: { // G4 pause uint32_t delay_ms = 0; if (gcode->has_letter('P')) { @@ -354,7 +386,7 @@ void Robot::on_gcode_received(void *argument) } if (delay_ms > 0) { // drain queue - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); // wait for specified time uint32_t start = us_ticker_read(); // mbed call while ((us_ticker_read() - start) < delay_ms * 1000) { @@ -390,10 +422,16 @@ void Robot::on_gcode_received(void *argument) } } else { - // the value is the offset from machine zero - if(gcode->has_letter('X')) x = to_millimeters(gcode->get_value('X')); - if(gcode->has_letter('Y')) y = to_millimeters(gcode->get_value('Y')); - if(gcode->has_letter('Z')) z = to_millimeters(gcode->get_value('Z')); + if(absolute_mode) { + // the value is the offset from machine zero + if(gcode->has_letter('X')) x = to_millimeters(gcode->get_value('X')); + if(gcode->has_letter('Y')) y = to_millimeters(gcode->get_value('Y')); + if(gcode->has_letter('Z')) z = to_millimeters(gcode->get_value('Z')); + }else{ + if(gcode->has_letter('X')) x += to_millimeters(gcode->get_value('X')); + if(gcode->has_letter('Y')) y += to_millimeters(gcode->get_value('Y')); + if(gcode->has_letter('Z')) z += to_millimeters(gcode->get_value('Z')); + } } wcs_offsets[n] = wcs_t(x, y, z); } @@ -415,8 +453,8 @@ void Robot::on_gcode_received(void *argument) } break; - case 90: this->absolute_mode = true; break; - case 91: this->absolute_mode = false; break; + case 90: this->absolute_mode = true; this->e_absolute_mode = true; break; + case 91: this->absolute_mode = false; this->e_absolute_mode = false; break; case 92: { if(gcode->subcode == 1 || gcode->subcode == 2 || gcode->get_num_args() == 0) { @@ -451,6 +489,22 @@ void Robot::on_gcode_received(void *argument) g92_offset = wcs_t(x, y, z); } + #if MAX_ROBOT_ACTUATORS > 3 + if(gcode->subcode == 0 && (gcode->has_letter('E') || gcode->get_num_args() == 0)){ + // reset the E position, legacy for 3d Printers to be reprap compatible + // find the selected extruder + // NOTE this will only work when E is 0 if volumetric and/or scaling is used as the actuator last milestone will be different if it was scaled + for (int i = E_AXIS; i < n_motors; ++i) { + if(actuators[i]->is_selected()) { + float e= gcode->has_letter('E') ? gcode->get_value('E') : 0; + last_milestone[i]= last_machine_position[i]= e; + actuators[i]->change_last_milestone(e); + break; + } + } + } + #endif + return; } } @@ -468,6 +522,41 @@ void Robot::on_gcode_received(void *argument) current_wcs = 0; absolute_mode = true; break; + case 17: + THEKERNEL->call_event(ON_ENABLE, (void*)1); // turn all enable pins on + break; + + case 18: // this allows individual motors to be turned off, no parameters falls through to turn all off + if(gcode->get_num_args() > 0) { + // bitmap of motors to turn off, where bit 1:X, 2:Y, 3:Z, 4:A, 5:B, 6:C + uint32_t bm= 0; + for (int i = 0; i < n_motors; ++i) { + char axis= (i <= Z_AXIS ? 'X'+i : 'A'+(i-3)); + if(gcode->has_letter(axis)) bm |= (0x02<has_letter('E')) { + for (int i = E_AXIS; i < n_motors; ++i) { + // find first selected extruder + if(actuators[i]->is_selected()) { + bm |= (0x02<conveyor->wait_for_idle(); + THEKERNEL->call_event(ON_ENABLE, (void *)bm); + break; + } + // fall through + case 84: + THEKERNEL->conveyor->wait_for_idle(); + THEKERNEL->call_event(ON_ENABLE, nullptr); // turn all enable pins off + break; + + case 82: e_absolute_mode= true; break; + case 83: e_absolute_mode= false; break; case 92: // M92 - set steps per mm if (gcode->has_letter('X')) @@ -477,7 +566,7 @@ void Robot::on_gcode_received(void *argument) if (gcode->has_letter('Z')) actuators[2]->change_steps_per_mm(this->to_millimeters(gcode->get_value('Z'))); - gcode->stream->printf("X:%f Y:%f Z:%f ", actuators[0]->steps_per_mm, actuators[1]->steps_per_mm, actuators[2]->steps_per_mm); + gcode->stream->printf("X:%f Y:%f Z:%f ", actuators[0]->get_steps_per_mm(), actuators[1]->get_steps_per_mm(), actuators[2]->get_steps_per_mm()); gcode->add_nl = true; check_max_actuator_speeds(); return; @@ -497,47 +586,55 @@ void Robot::on_gcode_received(void *argument) pop_state(); break; - case 203: // M203 Set maximum feedrates in mm/sec - if (gcode->has_letter('X')) - this->max_speeds[X_AXIS] = gcode->get_value('X'); - if (gcode->has_letter('Y')) - this->max_speeds[Y_AXIS] = gcode->get_value('Y'); - if (gcode->has_letter('Z')) - this->max_speeds[Z_AXIS] = gcode->get_value('Z'); - for (size_t i = 0; i < 3 && i < actuators.size(); i++) { - if (gcode->has_letter('A' + i)) - actuators[i]->set_max_rate(gcode->get_value('A' + i)); - } - check_max_actuator_speeds(); + case 203: // M203 Set maximum feedrates in mm/sec, M203.1 set maximum actuator feedrates + if(gcode->get_num_args() == 0) { + for (size_t i = X_AXIS; i <= Z_AXIS; i++) { + gcode->stream->printf(" %c: %g ", 'X' + i, gcode->subcode == 0 ? this->max_speeds[i] : actuators[i]->get_max_rate()); + } + gcode->add_nl = true; + + }else{ + for (size_t i = X_AXIS; i <= Z_AXIS; i++) { + if (gcode->has_letter('X' + i)) { + float v= gcode->get_value('X'+i); + if(gcode->subcode == 0) this->max_speeds[i]= v; + else if(gcode->subcode == 1) actuators[i]->set_max_rate(v); + } + } + + // this format is deprecated + if(gcode->subcode == 0 && (gcode->has_letter('A') || gcode->has_letter('B') || gcode->has_letter('C'))) { + gcode->stream->printf("NOTE this format is deprecated, Use M203.1 instead\n"); + for (size_t i = X_AXIS; i <= Z_AXIS; i++) { + if (gcode->has_letter('A' + i)) { + float v= gcode->get_value('A'+i); + actuators[i]->set_max_rate(v); + } + } + } - if(gcode->get_num_args() == 0) { - gcode->stream->printf("X:%g Y:%g Z:%g", - this->max_speeds[X_AXIS], this->max_speeds[Y_AXIS], this->max_speeds[Z_AXIS]); - for (size_t i = 0; i < 3 && i < actuators.size(); i++) { - gcode->stream->printf(" %c : %g", 'A' + i, actuators[i]->get_max_rate()); //xxx + if(gcode->subcode == 1) check_max_actuator_speeds(); } - gcode->add_nl = true; - } - break; + break; - case 204: // M204 Snnn - set acceleration to nnn, Znnn sets z acceleration + case 204: // M204 Snnn - set default acceleration to nnn, Xnnn Ynnn Znnn sets axis specific acceleration if (gcode->has_letter('S')) { float acc = gcode->get_value('S'); // mm/s^2 // enforce minimum - if (acc < 1.0F) - acc = 1.0F; - THEKERNEL->planner->acceleration = acc; + if (acc < 1.0F) acc = 1.0F; + this->default_acceleration = acc; } - if (gcode->has_letter('Z')) { - float acc = gcode->get_value('Z'); // mm/s^2 - // enforce positive - if (acc < 0.0F) - acc = 0.0F; - THEKERNEL->planner->z_acceleration = acc; + for (int i = X_AXIS; i <= Z_AXIS; ++i) { + if (gcode->has_letter(i+'X')) { + float acc = gcode->get_value(i+'X'); // mm/s^2 + // enforce positive + if (acc <= 0.0F) acc = NAN; + actuators[i]->set_acceleration(acc); + } } break; - case 205: // M205 Xnnn - set junction deviation, Z - set Z junction deviation, Snnn - Set minimum planner speed, Ynnn - set minimum step rate + case 205: // M205 Xnnn - set junction deviation, Z - set Z junction deviation, Snnn - Set minimum planner speed if (gcode->has_letter('X')) { float jd = gcode->get_value('X'); // enforce minimum @@ -548,8 +645,8 @@ void Robot::on_gcode_received(void *argument) if (gcode->has_letter('Z')) { float jd = gcode->get_value('Z'); // enforce minimum, -1 disables it and uses regular junction deviation - if (jd < -1.0F) - jd = -1.0F; + if (jd <= -1.0F) + jd = NAN; THEKERNEL->planner->z_junction_deviation = jd; } if (gcode->has_letter('S')) { @@ -578,21 +675,25 @@ void Robot::on_gcode_received(void *argument) break; case 400: // wait until all moves are done up to this point - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); break; case 500: // M500 saves some volatile settings to config override file case 503: { // M503 just prints the settings - gcode->stream->printf(";Steps per unit:\nM92 X%1.5f Y%1.5f Z%1.5f\n", actuators[0]->steps_per_mm, actuators[1]->steps_per_mm, actuators[2]->steps_per_mm); - gcode->stream->printf(";Acceleration mm/sec^2:\nM204 S%1.5f Z%1.5f\n", THEKERNEL->planner->acceleration, THEKERNEL->planner->z_acceleration); - gcode->stream->printf(";X- Junction Deviation, Z- Z junction deviation, S - Minimum Planner speed mm/sec:\nM205 X%1.5f Z%1.5f S%1.5f\n", THEKERNEL->planner->junction_deviation, THEKERNEL->planner->z_junction_deviation, THEKERNEL->planner->minimum_planner_speed); - gcode->stream->printf(";Max feedrates in mm/sec, XYZ cartesian, ABC actuator:\nM203 X%1.5f Y%1.5f Z%1.5f", - this->max_speeds[X_AXIS], this->max_speeds[Y_AXIS], this->max_speeds[Z_AXIS]); - for (size_t i = 0; i < 3 && i < actuators.size(); i++) { - gcode->stream->printf(" %c%1.5f", 'A' + i, actuators[i]->get_max_rate()); + gcode->stream->printf(";Steps per unit:\nM92 X%1.5f Y%1.5f Z%1.5f\n", actuators[0]->get_steps_per_mm(), actuators[1]->get_steps_per_mm(), actuators[2]->get_steps_per_mm()); + + // only print XYZ if not NAN + gcode->stream->printf(";Acceleration mm/sec^2:\nM204 S%1.5f ", default_acceleration); + for (int i = X_AXIS; i <= Z_AXIS; ++i) { + if(!isnan(actuators[i]->get_acceleration())) gcode->stream->printf("%c%1.5f ", 'X'+i, actuators[i]->get_acceleration()); } gcode->stream->printf("\n"); + gcode->stream->printf(";X- Junction Deviation, Z- Z junction deviation, S - Minimum Planner speed mm/sec:\nM205 X%1.5f Z%1.5f S%1.5f\n", THEKERNEL->planner->junction_deviation, isnan(THEKERNEL->planner->z_junction_deviation)?-1:THEKERNEL->planner->z_junction_deviation, THEKERNEL->planner->minimum_planner_speed); + + gcode->stream->printf(";Max cartesian feedrates in mm/sec:\nM203 X%1.5f Y%1.5f Z%1.5f\n", this->max_speeds[X_AXIS], this->max_speeds[Y_AXIS], this->max_speeds[Z_AXIS]); + gcode->stream->printf(";Max actuator feedrates in mm/sec:\nM203.1 X%1.5f Y%1.5f Z%1.5f\n", actuators[X_AXIS]->get_max_rate(), actuators[Y_AXIS]->get_max_rate(), actuators[Z_AXIS]->get_max_rate()); + // get or save any arm solution specific optional values BaseSolution::arm_options_t options; if(arm_solution->get_optional(options) && !options.empty()) { @@ -662,18 +763,25 @@ void Robot::on_gcode_received(void *argument) } } - if( this->motion_mode >= 0) { - process_move(gcode); + if( motion_mode != NONE) { + is_g123= motion_mode != SEEK; + process_move(gcode, motion_mode); + + }else{ + is_g123= false; } next_command_is_MCS = false; // must be on same line as G0 or G1 } // process a G0/G1/G2/G3 -void Robot::process_move(Gcode *gcode) +void Robot::process_move(Gcode *gcode, enum MOTION_MODE_T motion_mode) { // we have a G0/G1/G2/G3 so extract parameters and apply offsets to get machine coordinate target - float param[3]{NAN, NAN, NAN}; + // get XYZ and one E (which goes to the selected extruder) + float param[4]{NAN, NAN, NAN, NAN}; + + // process primary axis for(int i= X_AXIS; i <= Z_AXIS; ++i) { char letter= 'X'+i; if( gcode->has_letter(letter) ) { @@ -689,7 +797,9 @@ void Robot::process_move(Gcode *gcode) } // calculate target in machine coordinates (less compensation transform which needs to be done after segmentation) - float target[3]{last_milestone[X_AXIS], last_milestone[Y_AXIS], last_milestone[Z_AXIS]}; + float target[n_motors]; + memcpy(target, last_milestone, n_motors*sizeof(float)); + if(!next_command_is_MCS) { if(this->absolute_mode) { // apply wcs offsets and g92 offset and tool offset @@ -719,47 +829,70 @@ void Robot::process_move(Gcode *gcode) } } + // process extruder parameters, for active extruder only (only one active extruder at a time) + selected_extruder= 0; + if(gcode->has_letter('E')) { + for (int i = E_AXIS; i < n_motors; ++i) { + // find first selected extruder + if(actuators[i]->is_selected()) { + param[E_AXIS]= gcode->get_value('E'); + selected_extruder= i; + break; + } + } + } + + // do E for the selected extruder + float delta_e= NAN; + if(selected_extruder > 0 && !isnan(param[E_AXIS])) { + if(this->e_absolute_mode) { + target[selected_extruder]= param[E_AXIS]; + delta_e= target[selected_extruder] - last_milestone[selected_extruder]; + }else{ + delta_e= param[E_AXIS]; + target[selected_extruder] = delta_e + last_milestone[selected_extruder]; + } + } + if( gcode->has_letter('F') ) { - if( this->motion_mode == MOTION_MODE_SEEK ) + if( motion_mode == SEEK ) this->seek_rate = this->to_millimeters( gcode->get_value('F') ); else this->feed_rate = this->to_millimeters( gcode->get_value('F') ); } + // S is modal When specified on a G0/1/2/3 command + if(gcode->has_letter('S')) s_value= gcode->get_value('S'); + bool moved= false; - //Perform any physical actions - switch(this->motion_mode) { - case MOTION_MODE_CANCEL: - break; - case MOTION_MODE_SEEK: - moved= this->append_line(gcode, target, this->seek_rate / seconds_per_minute ); + + // Perform any physical actions + switch(motion_mode) { + case NONE: break; + + case SEEK: + moved= this->append_line(gcode, target, this->seek_rate / seconds_per_minute, delta_e ); break; - case MOTION_MODE_LINEAR: - moved= this->append_line(gcode, target, this->feed_rate / seconds_per_minute ); + + case LINEAR: + moved= this->append_line(gcode, target, this->feed_rate / seconds_per_minute, delta_e ); break; - case MOTION_MODE_CW_ARC: - case MOTION_MODE_CCW_ARC: - moved= this->compute_arc(gcode, offset, target ); + + case CW_ARC: + case CCW_ARC: + // Note arcs are not currently supported by extruder based machines, as 3D slicers do not use arcs (G2/G3) + moved= this->compute_arc(gcode, offset, target, motion_mode); break; } if(moved) { // set last_milestone to the calculated target - memcpy(this->last_milestone, target, sizeof(this->last_milestone)); + memcpy(last_milestone, target, n_motors*sizeof(float)); } } -// We received a new gcode, and one of the functions -// determined the distance for that given gcode. So now we can attach this gcode to the right block -// and continue -void Robot::distance_in_gcode_is_known(Gcode * gcode) -{ - //If the queue is empty, execute immediately, otherwise attach to the last added block - THEKERNEL->conveyor->append_gcode(gcode); -} - // reset the machine position for all axis. Used for homing. -// During homing compensation is turned off (actually not used as it drives steppers directly) +// During homing compensation is turned off // once homed and reset_axis called compensation is used for the move to origin and back off home if enabled, // so in those cases the final position is compensated. void Robot::reset_axis_position(float x, float y, float z) @@ -772,22 +905,29 @@ void Robot::reset_axis_position(float x, float y, float z) // now set the actuator positions to match ActuatorCoordinates actuator_pos; arm_solution->cartesian_to_actuator(this->last_machine_position, actuator_pos); - for (size_t i = 0; i < actuators.size(); i++) + for (size_t i = X_AXIS; i <= Z_AXIS; i++) actuators[i]->change_last_milestone(actuator_pos[i]); } -// Reset the position for an axis (used in homing) +// Reset the position for an axis (used in homing, and to reset extruder after suspend) void Robot::reset_axis_position(float position, int axis) { last_milestone[axis] = position; - reset_axis_position(last_milestone[X_AXIS], last_milestone[Y_AXIS], last_milestone[Z_AXIS]); + if(axis <= Z_AXIS) { + reset_axis_position(last_milestone[X_AXIS], last_milestone[Y_AXIS], last_milestone[Z_AXIS]); +#if MAX_ROBOT_ACTUATORS > 3 + }else{ + // extruders need to be set not calculated + last_machine_position[axis]= position; +#endif + } } // similar to reset_axis_position but directly sets the actuator positions in actuators units (eg mm for cartesian, degrees for rotary delta) -// then sets the axis positions to match. currently only called from Endstops.cpp +// then sets the axis positions to match. currently only called from Endstops.cpp and RotaryDeltaCalibration.cpp void Robot::reset_actuator_position(const ActuatorCoordinates &ac) { - for (size_t i = 0; i < actuators.size(); i++) + for (size_t i = X_AXIS; i <= Z_AXIS; i++) actuators[i]->change_last_milestone(ac[i]); // now correct axis positions then recorrect actuator to account for rounding @@ -798,7 +938,7 @@ void Robot::reset_actuator_position(const ActuatorCoordinates &ac) void Robot::reset_position_from_current_actuator_position() { ActuatorCoordinates actuator_pos; - for (size_t i = 0; i < actuators.size(); i++) { + for (size_t i = X_AXIS; i <= Z_AXIS; i++) { // NOTE actuator::current_position is curently NOT the same as actuator::last_milestone after an abrupt abort actuator_pos[i] = actuators[i]->get_current_position(); } @@ -812,30 +952,21 @@ void Robot::reset_position_from_current_actuator_position() // NOTE This is required to sync the machine position with the actuator position, we do a somewhat redundant cartesian_to_actuator() call // to get everything in perfect sync. arm_solution->cartesian_to_actuator(last_machine_position, actuator_pos); - for (size_t i = 0; i < actuators.size(); i++) + for (size_t i = X_AXIS; i <= Z_AXIS; i++) actuators[i]->change_last_milestone(actuator_pos[i]); } -// Convert target (in machine coordinates) from millimeters to steps, and append this to the planner +// Convert target (in machine coordinates) to machine_position, then convert to actuator position and append this to the planner // target is in machine coordinates without the compensation transform, however we save a last_machine_position that includes // all transforms and is what we actually convert to actuator positions -bool Robot::append_milestone(Gcode * gcode, const float target[], float rate_mm_s) +bool Robot::append_milestone(const float target[], float rate_mm_s) { - float deltas[3]; - float unit_vec[3]; - ActuatorCoordinates actuator_pos; - float transformed_target[3]; // adjust target for bed compensation and WCS offsets - float millimeters_of_travel; - - // catch negative or zero feed rates and return the same error as GRBL does - if(rate_mm_s <= 0.0F) { - gcode->is_error= true; - gcode->txt_after_ok= (rate_mm_s == 0 ? "Undefined feed rate" : "feed rate < 0"); - return false; - } + float deltas[n_motors]; + float transformed_target[n_motors]; // adjust target for bed compensation + float unit_vec[N_PRIMARY_AXIS]; // unity transform by default - memcpy(transformed_target, target, sizeof(transformed_target)); + memcpy(transformed_target, target, n_motors*sizeof(float)); // check function pointer and call if set to transform the target to compensate for bed if(compensationTransform) { @@ -843,82 +974,184 @@ bool Robot::append_milestone(Gcode * gcode, const float target[], float rate_mm_ compensationTransform(transformed_target); } + bool move= false; + float sos= 0; // sun of squares for just XYZ + // find distance moved by each axis, use transformed target from the current machine position - for (int axis = X_AXIS; axis <= Z_AXIS; axis++) { - deltas[axis] = transformed_target[axis] - last_machine_position[axis]; + for (size_t i = 0; i < n_motors; i++) { + deltas[i] = transformed_target[i] - last_machine_position[i]; + if(deltas[i] == 0) continue; + // at least one non zero delta + move = true; + if(i <= Z_AXIS) { + sos += powf(deltas[i], 2); + } } - // Compute how long this move moves, so we can attach it to the block for later use - millimeters_of_travel = sqrtf( powf( deltas[X_AXIS], 2 ) + powf( deltas[Y_AXIS], 2 ) + powf( deltas[Z_AXIS], 2 ) ); + // nothing moved + if(!move) return false; + + // see if this is a primary axis move or not + bool auxilliary_move= deltas[X_AXIS] == 0 && deltas[Y_AXIS] == 0 && deltas[Z_AXIS] == 0; + + // total movement, use XYZ if a primary axis otherwise we calculate distance for E after scaling to mm + float distance= auxilliary_move ? 0 : sqrtf(sos); // it is unlikely but we need to protect against divide by zero, so ignore insanely small moves here // as the last milestone won't be updated we do not actually lose any moves as they will be accounted for in the next move - if(millimeters_of_travel < 0.00001F) return false; + if(!auxilliary_move && distance < 0.00001F) return false; - // this is the machine position - memcpy(this->last_machine_position, transformed_target, sizeof(this->last_machine_position)); - // find distance unit vector - for (int i = 0; i < 3; i++) - unit_vec[i] = deltas[i] / millimeters_of_travel; + if(!auxilliary_move) { + for (size_t i = X_AXIS; i <= Z_AXIS; i++) { + // find distance unit vector for primary axis only + unit_vec[i] = deltas[i] / distance; - // Do not move faster than the configured cartesian limits - for (int axis = X_AXIS; axis <= Z_AXIS; axis++) { - if ( max_speeds[axis] > 0 ) { - float axis_speed = fabs(unit_vec[axis] * rate_mm_s); + // Do not move faster than the configured cartesian limits for XYZ + if ( max_speeds[i] > 0 ) { + float axis_speed = fabsf(unit_vec[i] * rate_mm_s); - if (axis_speed > max_speeds[axis]) - rate_mm_s *= ( max_speeds[axis] / axis_speed ); + if (axis_speed > max_speeds[i]) + rate_mm_s *= ( max_speeds[i] / axis_speed ); + } } } // find actuator position given the machine position, use actual adjusted target - arm_solution->cartesian_to_actuator( this->last_machine_position, actuator_pos ); + ActuatorCoordinates actuator_pos; + if(!disable_arm_solution) { + arm_solution->cartesian_to_actuator( transformed_target, actuator_pos ); + + }else{ + // basically the same as cartesian, would be used for special homing situations like for scara + for (size_t i = X_AXIS; i <= Z_AXIS; i++) { + actuator_pos[i] = transformed_target[i]; + } + } + +#if MAX_ROBOT_ACTUATORS > 3 + sos= 0; + // for the extruders just copy the position, and possibly scale it from mm³ to mm + for (size_t i = E_AXIS; i < n_motors; i++) { + actuator_pos[i]= transformed_target[i]; + if(get_e_scale_fnc) { + // NOTE this relies on the fact only one extruder is active at a time + // scale for volumetric or flow rate + // TODO is this correct? scaling the absolute target? what if the scale changes? + // for volumetric it basically converts mm³ to mm, but what about flow rate? + actuator_pos[i] *= get_e_scale_fnc(); + } + if(auxilliary_move) { + // for E only moves we need to use the scaled E to calculate the distance + sos += pow(actuator_pos[i] - actuators[i]->get_last_milestone(), 2); + } + } + if(auxilliary_move) { + distance= sqrtf(sos); // distance in mm of the e move + if(distance < 0.00001F) return false; + } +#endif + + // use default acceleration to start with + float acceleration = default_acceleration; + + float isecs = rate_mm_s / distance; - float isecs = rate_mm_s / millimeters_of_travel; // check per-actuator speed limits - for (size_t actuator = 0; actuator < actuators.size(); actuator++) { - float actuator_rate = fabsf(actuator_pos[actuator] - actuators[actuator]->last_milestone_mm) * isecs; + for (size_t actuator = 0; actuator < n_motors; actuator++) { + float d = fabsf(actuator_pos[actuator] - actuators[actuator]->get_last_milestone()); + if(d == 0 || !actuators[actuator]->is_selected()) continue; // no movement for this actuator + + float actuator_rate= d * isecs; if (actuator_rate > actuators[actuator]->get_max_rate()) { rate_mm_s *= (actuators[actuator]->get_max_rate() / actuator_rate); - isecs = rate_mm_s / millimeters_of_travel; + isecs = rate_mm_s / distance; + } + + // adjust acceleration to lowest found, for now just primary axis unless it is an auxiliary move + // TODO we may need to do all of them, check E won't limit XYZ.. it does on long E moves, but not checking it could exceed the E acceleration. + if(auxilliary_move || actuator <= Z_AXIS) { + float ma = actuators[actuator]->get_acceleration(); // in mm/sec² + if(!isnan(ma)) { // if axis does not have acceleration set then it uses the default_acceleration + float ca = fabsf((d/distance) * acceleration); + if (ca > ma) { + acceleration *= ( ma / ca ); + } + } } } // Append the block to the planner - THEKERNEL->planner->append_block( actuator_pos, rate_mm_s, millimeters_of_travel, unit_vec ); + // NOTE that distance here should be either the distance travelled by the XYZ axis, or the E mm travel if a solo E move + if(THEKERNEL->planner->append_block( actuator_pos, n_motors, rate_mm_s, distance, auxilliary_move ? nullptr : unit_vec, acceleration, s_value, is_g123)) { + // this is the machine position + memcpy(this->last_machine_position, transformed_target, n_motors*sizeof(float)); + return true; + } + + // no actual move + return false; +} + +// Used to plan a single move used by things like endstops when homing, zprobe, extruder firmware retracts etc. +bool Robot::delta_move(const float *delta, float rate_mm_s, uint8_t naxis) +{ + if(THEKERNEL->is_halted()) return false; + + // catch negative or zero feed rates + if(rate_mm_s <= 0.0F) { + return false; + } - return true; + // get the absolute target position, default is current last_milestone + float target[n_motors]; + memcpy(target, last_milestone, n_motors*sizeof(float)); + + // add in the deltas to get new target + for (int i= 0; i < naxis; i++) { + target[i] += delta[i]; + } + + // submit for planning and if moved update last_milestone + if(append_milestone(target, rate_mm_s)) { + memcpy(last_milestone, target, n_motors*sizeof(float)); + return true; + } + + return false; } // Append a move to the queue ( cutting it into segments if needed ) -bool Robot::append_line(Gcode *gcode, const float target[], float rate_mm_s ) +bool Robot::append_line(Gcode *gcode, const float target[], float rate_mm_s, float delta_e) { - // Find out the distance for this move in MCS - // NOTE we need to do sqrt here as this setting of millimeters_of_travel is used by extruder and other modules even if there is no XYZ move - gcode->millimeters_of_travel = sqrtf(powf( target[X_AXIS] - last_milestone[X_AXIS], 2 ) + powf( target[Y_AXIS] - last_milestone[Y_AXIS], 2 ) + powf( target[Z_AXIS] - last_milestone[Z_AXIS], 2 )); - - // We ignore non- XYZ moves ( for example, extruder moves are not XYZ moves ) - if( gcode->millimeters_of_travel < 0.00001F ) return false; - - // Mark the gcode as having a known distance - this->distance_in_gcode_is_known( gcode ); - - // if we have volumetric limits enabled we calculate the volume for this move and limit the rate if it exceeds the stated limit - // Note we need to be using volumetric extrusion for this to work as Ennn is in mm³ not mm - // We also check we are not exceeding the E max_speed for the current extruder - // We ask Extruder to do all the work, but as Extruder won't even see this gcode until after it has been planned - // we need to ask it now passing in the relevant data. - // NOTE we need to do this before we segment the line (for deltas) - /* TOADDBACK When extruder is implemented if(gcode->has_letter('E')) { - float data[2]; - data[0] = gcode->get_value('E'); // E target (may be absolute or relative) - data[1] = rate_mm_s / gcode->millimeters_of_travel; // inverted seconds for the move + // catch negative or zero feed rates and return the same error as GRBL does + if(rate_mm_s <= 0.0F) { + gcode->is_error= true; + gcode->txt_after_ok= (rate_mm_s == 0 ? "Undefined feed rate" : "feed rate < 0"); + return false; + } + + // Find out the distance for this move in XYZ in MCS + float millimeters_of_travel = sqrtf(powf( target[X_AXIS] - last_milestone[X_AXIS], 2 ) + powf( target[Y_AXIS] - last_milestone[Y_AXIS], 2 ) + powf( target[Z_AXIS] - last_milestone[Z_AXIS], 2 )); + + if(millimeters_of_travel < 0.00001F) { + // we have no movement in XYZ, probably E only extrude or retract + return this->append_milestone(target, rate_mm_s); + } + + /* + For extruders, we need to do some extra work to limit the volumetric rate if specified... + If using volumetric limts we need to be using volumetric extrusion for this to work as Ennn needs to be in mm³ not mm + We ask Extruder to do all the work but we need to pass in the relevant data. + NOTE we need to do this before we segment the line (for deltas) + */ + if(!isnan(delta_e) && gcode->has_g && gcode->g == 1) { + float data[2]= {delta_e, rate_mm_s / millimeters_of_travel}; if(PublicData::set_value(extruder_checksum, target_checksum, data)) { - rate_mm_s *= data[1]; - //THEKERNEL->streams->printf("Extruder has changed the rate by %f to %f\n", data[1], rate_mm_s); + rate_mm_s *= data[1]; // adjust the feedrate } - } */ + } + // We cut the line into smaller segments. This is only needed on a cartesian robot for zgrid, but always necessary for robots with rotational axes like Deltas. // In delta robots either mm_per_line_segment can be used OR delta_segments_per_second // The latter is more efficient and avoids splitting fast long lines into very small segments, like initial z move to 0, it is what Johanns Marlin delta port does @@ -932,7 +1165,7 @@ bool Robot::append_line(Gcode *gcode, const float target[], float rate_mm_s ) // segment based on current speed and requested segments per second // the faster the travel speed the fewer segments needed // NOTE rate is mm/sec and we take into account any speed override - float seconds = gcode->millimeters_of_travel / rate_mm_s; + float seconds = millimeters_of_travel / rate_mm_s; segments = max(1.0F, ceilf(this->delta_segments_per_second * seconds)); // TODO if we are only moving in Z on a delta we don't really need to segment at all @@ -940,50 +1173,54 @@ bool Robot::append_line(Gcode *gcode, const float target[], float rate_mm_s ) if(this->mm_per_line_segment == 0.0F) { segments = 1; // don't split it up } else { - segments = ceilf( gcode->millimeters_of_travel / this->mm_per_line_segment); + segments = ceilf( millimeters_of_travel / this->mm_per_line_segment); } } bool moved= false; if (segments > 1) { // A vector to keep track of the endpoint of each segment - float segment_delta[3]; - float segment_end[3]{last_milestone[X_AXIS], last_milestone[Y_AXIS], last_milestone[Z_AXIS]}; + float segment_delta[n_motors]; + float segment_end[n_motors]; + memcpy(segment_end, last_milestone, n_motors*sizeof(float)); // How far do we move each segment? - for (int i = X_AXIS; i <= Z_AXIS; i++) + for (int i = 0; i < n_motors; i++) segment_delta[i] = (target[i] - last_milestone[i]) / segments; // segment 0 is already done - it's the end point of the previous move so we start at segment 1 // We always add another point after this loop so we stop at segments-1, ie i < segments for (int i = 1; i < segments; i++) { if(THEKERNEL->is_halted()) return false; // don't queue any more segments - for(int axis = X_AXIS; axis <= Z_AXIS; axis++ ) - segment_end[axis] += segment_delta[axis]; + for (int i = 0; i < n_motors; i++) + segment_end[i] += segment_delta[i]; // Append the end of this segment to the queue - bool b= this->append_milestone(gcode, segment_end, rate_mm_s); + bool b= this->append_milestone(segment_end, rate_mm_s); moved= moved || b; } } // Append the end of this full move to the queue - if(this->append_milestone(gcode, target, rate_mm_s)) moved= true; + if(this->append_milestone(target, rate_mm_s)) moved= true; this->next_command_is_MCS = false; // always reset this - if(moved) { - // if adding these blocks didn't start executing, do that now - THEKERNEL->conveyor->ensure_running(); - } - return moved; } // Append an arc to the queue ( cutting it into segments as needed ) +// TODO does not support any E parameters so cannot be used for 3D printing. bool Robot::append_arc(Gcode * gcode, const float target[], const float offset[], float radius, bool is_clockwise ) { + float rate_mm_s= this->feed_rate / seconds_per_minute; + // catch negative or zero feed rates and return the same error as GRBL does + if(rate_mm_s <= 0.0F) { + gcode->is_error= true; + gcode->txt_after_ok= (rate_mm_s == 0 ? "Undefined feed rate" : "feed rate < 0"); + return false; + } // Scary math float center_axis0 = this->last_milestone[this->plane_axis_0] + offset[this->plane_axis_0]; @@ -998,25 +1235,32 @@ bool Robot::append_arc(Gcode * gcode, const float target[], const float offset[] // CCW angle between position and target from circle center. Only one atan2() trig computation required. float angular_travel = atan2f(r_axis0 * rt_axis1 - r_axis1 * rt_axis0, r_axis0 * rt_axis0 + r_axis1 * rt_axis1); if (is_clockwise) { // Correct atan2 output per direction - if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel -= (2 * (float)M_PI); } + if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel -= (2 * PI); } } else { - if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel += (2 * (float)M_PI); } + if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel += (2 * PI); } } // Find the distance for this gcode - gcode->millimeters_of_travel = hypotf(angular_travel * radius, fabsf(linear_travel)); + float millimeters_of_travel = hypotf(angular_travel * radius, fabsf(linear_travel)); // We don't care about non-XYZ moves ( for example the extruder produces some of those ) - if( gcode->millimeters_of_travel < 0.00001F ) { + if( millimeters_of_travel < 0.00001F ) { return false; } - // Mark the gcode as having a known distance - this->distance_in_gcode_is_known( gcode ); - + // limit segments by maximum arc error + float arc_segment = this->mm_per_arc_segment; + if ((this->mm_max_arc_error > 0) && (2 * radius > this->mm_max_arc_error)) { + float min_err_segment = 2 * sqrtf((this->mm_max_arc_error * (2 * radius - this->mm_max_arc_error))); + if (this->mm_per_arc_segment < min_err_segment) { + arc_segment = min_err_segment; + } + } // Figure out how many segments for this gcode - uint16_t segments = floorf(gcode->millimeters_of_travel / this->mm_per_arc_segment); + // TODO for deltas we need to make sure we are at least as many segments as requested, also if mm_per_line_segment is set we need to use the + uint16_t segments = ceilf(millimeters_of_travel / arc_segment); + //printf("Radius %f - Segment Length %f - Number of Segments %d\r\n",radius,arc_segment,segments); // Testing Purposes ONLY float theta_per_segment = angular_travel / segments; float linear_per_segment = linear_travel / segments; @@ -1083,18 +1327,18 @@ bool Robot::append_arc(Gcode * gcode, const float target[], const float offset[] arc_target[this->plane_axis_2] += linear_per_segment; // Append this segment to the queue - bool b= this->append_milestone(gcode, arc_target, this->feed_rate / seconds_per_minute); + bool b= this->append_milestone(arc_target, rate_mm_s); moved= moved || b; } // Ensure last segment arrives at target location. - if(this->append_milestone(gcode, target, this->feed_rate / seconds_per_minute)) moved= true; + if(this->append_milestone(target, rate_mm_s)) moved= true; return moved; } // Do the math for an arc and add it to the queue -bool Robot::compute_arc(Gcode * gcode, const float offset[], const float target[]) +bool Robot::compute_arc(Gcode * gcode, const float offset[], const float target[], enum MOTION_MODE_T motion_mode) { // Find the radius @@ -1102,7 +1346,7 @@ bool Robot::compute_arc(Gcode * gcode, const float offset[], const float target[ // Set clockwise/counter-clockwise sign for mc_arc computations bool is_clockwise = false; - if( this->motion_mode == MOTION_MODE_CW_ARC ) { + if( motion_mode == CW_ARC ) { is_clockwise = true; } @@ -1118,9 +1362,9 @@ float Robot::theta(float x, float y) return(t); } else { if (t > 0) { - return(M_PI - t); + return(PI - t); } else { - return(-M_PI - t); + return(-PI - t); } } } diff --git a/src/modules/robot/Robot.h b/src/modules/robot/Robot.h index 16a8d1b0..e2a44577 100644 --- a/src/modules/robot/Robot.h +++ b/src/modules/robot/Robot.h @@ -17,6 +17,7 @@ using std::string; #include "libs/Module.h" #include "ActuatorCoordinates.h" +#include "nuts_bolts.h" #include "Pin.h" #include @@ -26,6 +27,7 @@ class StepperMotor; // 9 WCS offsets #define MAX_WCS 9UL +#define N_PRIMARY_AXIS 3 class Robot : public Module { public: @@ -39,54 +41,73 @@ class Robot : public Module { void reset_actuator_position(const ActuatorCoordinates &ac); void reset_position_from_current_actuator_position(); float get_seconds_per_minute() const { return seconds_per_minute; } - float get_z_maxfeedrate() const { return this->max_speeds[2]; } - void setToolOffset(const float offset[3]); + float get_z_maxfeedrate() const { return this->max_speeds[Z_AXIS]; } + float get_default_acceleration() const { return default_acceleration; } + void setToolOffset(const float offset[N_PRIMARY_AXIS]); float get_feed_rate() const; + float get_s_value() const { return s_value; } + void set_s_value(float s) { s_value= s; } void push_state(); void pop_state(); void check_max_actuator_speeds(); float to_millimeters( float value ) const { return this->inch_mode ? value * 25.4F : value; } float from_millimeters( float value) const { return this->inch_mode ? value/25.4F : value; } - void get_axis_position(float position[]) const { memcpy(position, this->last_milestone, sizeof this->last_milestone); } - wcs_t get_axis_position() const { return wcs_t(last_milestone[0], last_milestone[1], last_milestone[2]); } + float get_axis_position(int axis) const { return(this->last_milestone[axis]); } + void get_axis_position(float position[], size_t n= N_PRIMARY_AXIS) const { memcpy(position, this->last_milestone, n*sizeof(float)); } + wcs_t get_axis_position() const { return wcs_t(last_milestone[X_AXIS], last_milestone[Y_AXIS], last_milestone[Z_AXIS]); } int print_position(uint8_t subcode, char *buf, size_t bufsize) const; uint8_t get_current_wcs() const { return current_wcs; } std::vector get_wcs_state() const; std::tuple get_last_probe_position() const { return last_probe_position; } void set_last_probe_position(std::tuple p) { last_probe_position = p; } + bool delta_move(const float delta[], float rate_mm_s, uint8_t naxis); + uint8_t register_motor(StepperMotor*); + uint8_t get_number_registered_motors() const {return n_motors; } BaseSolution* arm_solution; // Selected Arm solution ( millimeters to step calculation ) // gets accessed by Panel, Endstops, ZProbe - std::array actuators; + std::vector actuators; // set by a leveling strategy to transform the target of a move according to the current plan std::function compensationTransform; + // set by an active extruder, returns the amount tio scale the E parameter by (to convert mm³ to mm) + std::function get_e_scale_fnc; // Workspace coordinate systems wcs_t mcs2wcs(const wcs_t &pos) const; - wcs_t mcs2wcs(const float *pos) const { return mcs2wcs(wcs_t(pos[0], pos[1], pos[2])); } + wcs_t mcs2wcs(const float *pos) const { return mcs2wcs(wcs_t(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS])); } struct { bool inch_mode:1; // true for inch mode, false for millimeter mode ( default ) bool absolute_mode:1; // true for absolute mode ( default ), false for relative mode + bool e_absolute_mode:1; // true for absolute mode for E ( default ), false for relative mode bool next_command_is_MCS:1; // set by G53 bool disable_segmentation:1; // set to disable segmentation + bool disable_arm_solution:1; // set to disable the arm solution bool segment_z_moves:1; bool save_g92:1; // save g92 on M500 if set + bool is_g123:1; uint8_t plane_axis_0:2; // Current plane ( XY, XZ, YZ ) uint8_t plane_axis_1:2; uint8_t plane_axis_2:2; }; private: + enum MOTION_MODE_T { + NONE, + SEEK, // G0 + LINEAR, // G1 + CW_ARC, // G2 + CCW_ARC // G3 + }; + void load_config(); - void distance_in_gcode_is_known(Gcode* gcode); - bool append_milestone( Gcode *gcode, const float target[], float rate_mm_s); - bool append_line( Gcode* gcode, const float target[], float rate_mm_s); + bool append_milestone(const float target[], float rate_mm_s); + bool append_line( Gcode* gcode, const float target[], float rate_mm_s, float delta_e); bool append_arc( Gcode* gcode, const float target[], const float offset[], float radius, bool is_clockwise ); - bool compute_arc(Gcode* gcode, const float offset[], const float target[]); - void process_move(Gcode *gcode); + bool compute_arc(Gcode* gcode, const float offset[], const float target[], enum MOTION_MODE_T motion_mode); + void process_move(Gcode *gcode, enum MOTION_MODE_T); float theta(float x, float y); void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2); @@ -99,26 +120,33 @@ class Robot : public Module { wcs_t tool_offset; // used for multiple extruders, sets the tool offset for the current extruder applied first std::tuple last_probe_position{0,0,0,0}; - using saved_state_t= std::tuple; // save current feedrate and absolute mode, inch mode, current_wcs + using saved_state_t= std::tuple; // save current feedrate and absolute mode, e absolute mode, inch mode, current_wcs std::stack state_stack; // saves state from M120 - float last_milestone[3]; // Last requested position, in millimeters, which is what we were requested to move to in the gcode after offsets applied but before compensation transform - float last_machine_position[3]; // Last machine position, which is the position before converting to actuator coordinates (includes compensation transform) - int8_t motion_mode; // Motion mode for the current received Gcode + float last_milestone[k_max_actuators]; // Last requested position, in millimeters, which is what we were requested to move to in the gcode after offsets applied but before compensation transform + float last_machine_position[k_max_actuators]; // Last machine position, which is the position before converting to actuator coordinates (includes compensation transform) + float seek_rate; // Current rate for seeking moves ( mm/min ) float feed_rate; // Current rate for feeding moves ( mm/min ) float mm_per_line_segment; // Setting : Used to split lines into segments float mm_per_arc_segment; // Setting : Used to split arcs into segments + float mm_max_arc_error; // Setting : Used to limit total arc segments to max error float delta_segments_per_second; // Setting : Used to split lines into segments for delta based on speed float seconds_per_minute; // for realtime speed change + float default_acceleration; // the defualt accleration if not set for each axis + float s_value; // modal S value // Number of arc generation iterations by small angle approximation before exact arc trajectory // correction. This parameter may be decreased if there are issues with the accuracy of the arc // generations. In general, the default value is more than enough for the intended CNC applications // of grbl, and should be on the order or greater than the size of the buffer to help with the // computational efficiency of generating arcs. - int arc_correction; // Setting : how often to rectify arc computation - float max_speeds[3]; // Setting : max allowable speed in mm/m for each axis + int arc_correction; // Setting : how often to rectify arc computation + float max_speeds[3]; // Setting : max allowable speed in mm/s for each axis + + uint8_t selected_extruder; + uint8_t n_motors; //count + //used to initialize the TMC2130 stepper motor driver if present mbed::SPI *spi; diff --git a/src/modules/robot/Stepper.cpp b/src/modules/robot/Stepper.cpp deleted file mode 100644 index 243311f1..00000000 --- a/src/modules/robot/Stepper.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl) - Smoothie 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. - Smoothie 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 Smoothie. If not, see . -*/ - -#include "Stepper.h" - -#include "libs/Module.h" -#include "libs/Kernel.h" -#include "Planner.h" -#include "Conveyor.h" -#include "StepperMotor.h" -#include "Robot.h" -#include "checksumm.h" -#include "SlowTicker.h" -#include "Config.h" -#include "ConfigValue.h" -#include "Gcode.h" -#include "Block.h" -#include "StepTicker.h" - -#include -using namespace std; - -#include "libs/nuts_bolts.h" -#include "libs/Hook.h" - -#include - -// The stepper reacts to blocks that have XYZ movement to transform them into actual stepper motor moves -// TODO: This does accel, accel should be in StepperMotor - -Stepper::Stepper() -{ - this->current_block = nullptr; - this->force_speed_update = false; - trapezoid_adjusted_rate = 0; - main_stepper = nullptr; -} - -//Called when the module has just been loaded -void Stepper::on_module_loaded() -{ - this->register_for_event(ON_BLOCK_BEGIN); - this->register_for_event(ON_BLOCK_END); - this->register_for_event(ON_GCODE_RECEIVED); - this->register_for_event(ON_HALT); - - // Get onfiguration - this->on_config_reload(this); - - // Acceleration ticker - THEKERNEL->step_ticker->register_acceleration_tick_handler([this](){trapezoid_generator_tick(); }); - - // Attach to the end_of_move stepper event - for (auto actuator : THEKERNEL->robot->actuators) - actuator->attach(this, &Stepper::stepper_motor_finished_move ); -} - -// Get configuration from the config file -void Stepper::on_config_reload(void *argument) -{ - // Steppers start off by default - this->turn_enable_pins_off(); -} - -void Stepper::on_halt(void *argument) -{ - if(argument == nullptr) { - this->turn_enable_pins_off(); - } -} - -void Stepper::on_gcode_received(void *argument) -{ - Gcode *gcode = static_cast(argument); - - if( gcode->has_m) { - if( gcode->m == 17 ) { - this->turn_enable_pins_on(); - - }else if( (gcode->m == 84 || gcode->m == 18) && !gcode->has_letter('E') ) { - THEKERNEL->conveyor->wait_for_empty_queue(); - this->turn_enable_pins_off(); - } - } -} - -// Enable steppers -void Stepper::turn_enable_pins_on() -{ - for (auto a : THEKERNEL->robot->actuators) - a->enable(true); - this->enable_pins_status = true; - THEKERNEL->call_event(ON_ENABLE, (void*)1); -} - -// Disable steppers -void Stepper::turn_enable_pins_off() -{ - for (auto a : THEKERNEL->robot->actuators) - a->enable(false); - this->enable_pins_status = false; - THEKERNEL->call_event(ON_ENABLE, nullptr); -} - -// A new block is popped from the queue -void Stepper::on_block_begin(void *argument) -{ - Block *block = static_cast(argument); - - // Mark the new block as of interrest to us, handle blocks that have no axis moves properly (like Extrude blocks etc) - bool take = false; - if (block->millimeters > 0.0F) { - for (size_t s = 0; !take && s < THEKERNEL->robot->actuators.size(); s++) { - take = block->steps[s] > 0; - } - } - if (take){ - block->take(); - } else { - // none of the steppers move this block so make sure they know that - for(auto a : THEKERNEL->robot->actuators) { - a->set_moved_last_block(false); - } - return; - } - - // We can't move with the enable pins off - if( this->enable_pins_status == false ) { - this->turn_enable_pins_on(); - } - - // Setup : instruct stepper motors to move - // Find the stepper with the more steps, it's the one the speed calculations will want to follow - this->main_stepper = nullptr; - int most_steps_to_move = 0; - for (size_t i = 0; i < THEKERNEL->robot->actuators.size(); i++) { - if (block->steps[i] > 0) { - THEKERNEL->robot->actuators[i]->move(block->direction_bits[i], block->steps[i])->set_moved_last_block(true); - int steps_to_move = THEKERNEL->robot->actuators[i]->get_steps_to_move(); - if (steps_to_move > most_steps_to_move) { - most_steps_to_move = steps_to_move; - this->main_stepper = THEKERNEL->robot->actuators[i]; - } - } - else { - THEKERNEL->robot->actuators[i]->set_moved_last_block(false); - } - } - - this->current_block = block; - - // Setup acceleration for this block - this->trapezoid_generator_reset(); - - // Set the initial speed for this move - this->trapezoid_generator_tick(); - - // synchronize the acceleration timer with the start of the new block so it does not drift and randomly fire during the block - THEKERNEL->step_ticker->synchronize_acceleration(false); - - // set a flag to synchronize the acceleration timer with the deceleration step, and fire it immediately we get to that step - if( block->decelerate_after > 0 && block->decelerate_after+1 < this->main_stepper->steps_to_move ) { - this->main_stepper->signal_step= block->decelerate_after+1; // we make it +1 as deceleration does not start until steps > decelerate_after - } -} - -// Current block is discarded -void Stepper::on_block_end(void *argument) -{ - this->current_block = NULL; //stfu ! -} - -// When a stepper motor has finished it's assigned movement -uint32_t Stepper::stepper_motor_finished_move(uint32_t dummy) -{ - // We care only if none is still moving - for (auto a : THEKERNEL->robot->actuators) { - if(a->moving) - return 0; - } - - // This block is finished, release it - if( this->current_block != NULL ) { - this->current_block->release(); - } - - return 0; -} - - -// This is called ACCELERATION_TICKS_PER_SECOND times per second by the step_event -// interrupt. It can be assumed that the trapezoid-generator-parameters and the -// current_block stays untouched by outside handlers for the duration of this function call. -// NOTE caled at the same priority as PendSV so it may make that longer but it is better that having htis pre empted by pendsv -void Stepper::trapezoid_generator_tick(void) -{ - // Do not do the accel math for nothing - if(this->current_block && this->main_stepper->moving ) { - - // Store this here because we use it a lot down there - uint32_t current_steps_completed = this->main_stepper->stepped; - float last_rate= trapezoid_adjusted_rate; - - if( this->force_speed_update ) { - // Do not accel, just set the value - this->force_speed_update = false; - last_rate= -1; - - } else if(THEKERNEL->conveyor->is_flushing()) { - // if we are flushing the queue, decelerate to 0 then finish this block - if (trapezoid_adjusted_rate > current_block->rate_delta * 1.5F) { - trapezoid_adjusted_rate -= current_block->rate_delta; - - } else if (trapezoid_adjusted_rate == current_block->rate_delta * 0.5F) { - for (auto i : THEKERNEL->robot->actuators) i->move(i->direction, 0); // stop motors - if (current_block) current_block->release(); - THEKERNEL->call_event(ON_SPEED_CHANGE, 0); // tell others we stopped - return; - - } else { - trapezoid_adjusted_rate = current_block->rate_delta * 0.5F; - } - - } else if(current_steps_completed <= this->current_block->accelerate_until) { - // If we are accelerating - // Increase speed - this->trapezoid_adjusted_rate += this->current_block->rate_delta; - if (this->trapezoid_adjusted_rate > this->current_block->nominal_rate ) { - this->trapezoid_adjusted_rate = this->current_block->nominal_rate; - } - - } else if (current_steps_completed > this->current_block->decelerate_after) { - // If we are decelerating - // Reduce speed - // NOTE: We will only reduce speed if the result will be > 0. This catches small - // rounding errors that might leave steps hanging after the last trapezoid tick. - if(this->trapezoid_adjusted_rate > this->current_block->rate_delta * 1.5F) { - this->trapezoid_adjusted_rate -= this->current_block->rate_delta; - } else { - this->trapezoid_adjusted_rate = this->current_block->rate_delta * 1.5F; - } - if(this->trapezoid_adjusted_rate < this->current_block->final_rate ) { - this->trapezoid_adjusted_rate = this->current_block->final_rate; - } - - } else if (trapezoid_adjusted_rate != current_block->nominal_rate) { - // If we are cruising - // Make sure we cruise at exactly nominal rate - this->trapezoid_adjusted_rate = this->current_block->nominal_rate; - } - - if(last_rate != trapezoid_adjusted_rate) { - // don't call this if speed did not change - this->set_step_events_per_second(this->trapezoid_adjusted_rate); - } - } -} - -// Initializes the trapezoid generator from the current block. Called whenever a new -// block begins. -inline void Stepper::trapezoid_generator_reset() -{ - this->trapezoid_adjusted_rate = this->current_block->initial_rate; - this->force_speed_update = true; -} - -// Update the speed for all steppers -void Stepper::set_step_events_per_second( float steps_per_second ) -{ - float isps= steps_per_second / this->current_block->steps_event_count; - - // Instruct the stepper motors - for (size_t i = 0; i < THEKERNEL->robot->actuators.size(); i++) { - if (THEKERNEL->robot->actuators[i]->moving) { - THEKERNEL->robot->actuators[i]->set_speed(isps * this->current_block->steps[i]); - } - } - - // Other modules might want to know the speed changed - THEKERNEL->call_event(ON_SPEED_CHANGE, this); -} - - diff --git a/src/modules/robot/Stepper.h b/src/modules/robot/Stepper.h deleted file mode 100644 index f1882333..00000000 --- a/src/modules/robot/Stepper.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). - Smoothie 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. - Smoothie 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 Smoothie. If not, see . -*/ - -#ifndef STEPPER_H -#define STEPPER_H - -#include "libs/Module.h" -#include - -class Block; -class StepperMotor; - -class Stepper : public Module -{ -public: - Stepper(); - void on_module_loaded(); - void on_config_reload(void *argument); - void on_block_begin(void *argument); - void on_block_end(void *argument); - void on_gcode_received(void *argument); - void on_halt(void *argument); - - void trapezoid_generator_reset(); - void set_step_events_per_second(float); - void trapezoid_generator_tick(void); - uint32_t stepper_motor_finished_move(uint32_t dummy); - int config_step_timer( int cycles ); - void turn_enable_pins_on(); - void turn_enable_pins_off(); - - float get_trapezoid_adjusted_rate() const { return trapezoid_adjusted_rate; } - const Block *get_current_block() const { return current_block; } - -private: - Block *current_block; - float trapezoid_adjusted_rate; - StepperMotor *main_stepper; - - struct { - bool enable_pins_status:1; - bool force_speed_update:1; - }; - -}; - - - - -#endif diff --git a/src/modules/tools/drillingcycles/Drillingcycles.cpp b/src/modules/tools/drillingcycles/Drillingcycles.cpp index 07446c2e..399e6ae2 100644 --- a/src/modules/tools/drillingcycles/Drillingcycles.cpp +++ b/src/modules/tools/drillingcycles/Drillingcycles.cpp @@ -16,12 +16,9 @@ #include "SlowTicker.h" #include "StepperMotor.h" #include "StreamOutputPool.h" -#include /* fmod */ +#include "nuts_bolts.h" -// axis index -#define X_AXIS 0 -#define Y_AXIS 1 -#define Z_AXIS 2 +#include /* fmod */ // retract modes #define RETRACT_TO_Z 0 @@ -204,12 +201,12 @@ void Drillingcycles::on_gcode_received(void* argument) // cycle start if (code == 98 || code == 99) { // wait for any moves left and current position is update - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); // get actual position from robot float pos[3]; - THEKERNEL->robot->get_axis_position(pos); + THEROBOT->get_axis_position(pos); // convert to WCS - Robot::wcs_t wpos= THEKERNEL->robot->mcs2wcs(pos); + Robot::wcs_t wpos= THEROBOT->mcs2wcs(pos); // backup Z position as Initial-Z value this->initial_z = std::get(wpos); // must use the work coordinate position // set retract type @@ -233,7 +230,7 @@ void Drillingcycles::on_gcode_received(void* argument) // in cycle else if (this->cycle_started) { // relative mode not supported for now... - if (THEKERNEL->robot->absolute_mode == false) { + if (THEROBOT->absolute_mode == false) { gcode->stream->printf("Drillingcycles: relative mode not supported.\r\n"); gcode->stream->printf("Drillingcycles: skip hole...\r\n"); // exit diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index cec39acf..3ec9055a 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -16,7 +16,6 @@ #include "libs/StepperMotor.h" #include "wait_api.h" // mbed.h lib #include "Robot.h" -#include "Stepper.h" #include "Config.h" #include "SlowTicker.h" #include "Planner.h" @@ -59,6 +58,10 @@ #define beta_trim_checksum CHECKSUM("beta_trim") #define gamma_trim_checksum CHECKSUM("gamma_trim") +#define alpha_max_travel_checksum CHECKSUM("alpha_max_travel") +#define beta_max_travel_checksum CHECKSUM("beta_max_travel") +#define gamma_max_travel_checksum CHECKSUM("gamma_max_travel") + // these values are in steps and should be deprecated #define alpha_fast_homing_rate_checksum CHECKSUM("alpha_fast_homing_rate") #define beta_fast_homing_rate_checksum CHECKSUM("beta_fast_homing_rate") @@ -86,12 +89,12 @@ #define gamma_homing_retract_mm_checksum CHECKSUM("gamma_homing_retract_mm") #define endstop_debounce_count_checksum CHECKSUM("endstop_debounce_count") +#define endstop_debounce_ms_checksum CHECKSUM("endstop_debounce_ms") #define alpha_homing_direction_checksum CHECKSUM("alpha_homing_direction") #define beta_homing_direction_checksum CHECKSUM("beta_homing_direction") #define gamma_homing_direction_checksum CHECKSUM("gamma_homing_direction") -#define home_to_max_checksum CHECKSUM("home_to_max") -#define home_to_min_checksum CHECKSUM("home_to_min") + #define alpha_min_checksum CHECKSUM("alpha_min") #define beta_min_checksum CHECKSUM("beta_min") #define gamma_min_checksum CHECKSUM("gamma_min") @@ -104,18 +107,19 @@ #define beta_limit_enable_checksum CHECKSUM("beta_limit_enable") #define gamma_limit_enable_checksum CHECKSUM("gamma_limit_enable") +#define home_z_first_checksum CHECKSUM("home_z_first") #define homing_order_checksum CHECKSUM("homing_order") #define move_to_origin_checksum CHECKSUM("move_to_origin_after_home") -#define STEPPER THEKERNEL->robot->actuators +#define STEPPER THEROBOT->actuators #define STEPS_PER_MM(a) (STEPPER[a]->get_steps_per_mm()) // Homing States enum { MOVING_TO_ENDSTOP_FAST, // homing move - MOVING_BACK, // homing move MOVING_TO_ENDSTOP_SLOW, // homing move + MOVING_BACK, // homing move NOT_HOMING, BACK_OFF_HOME, MOVE_TO_ORIGIN, @@ -126,6 +130,7 @@ Endstops::Endstops() { this->status = NOT_HOMING; home_offset[0] = home_offset[1] = home_offset[2] = 0.0F; + debounce.fill(0); } void Endstops::on_module_loaded() @@ -140,10 +145,10 @@ void Endstops::on_module_loaded() register_for_event(ON_GET_PUBLIC_DATA); register_for_event(ON_SET_PUBLIC_DATA); - THEKERNEL->step_ticker->register_acceleration_tick_handler([this]() {acceleration_tick(); }); - // Settings this->load_config(); + + THEKERNEL->slow_ticker->attach(1000, this, &Endstops::read_endstops); } // Get config @@ -178,27 +183,37 @@ void Endstops::load_config() this->retract_mm[1] = THEKERNEL->config->value(beta_homing_retract_mm_checksum )->by_default(this->retract_mm[1])->as_number(); this->retract_mm[2] = THEKERNEL->config->value(gamma_homing_retract_mm_checksum )->by_default(this->retract_mm[2])->as_number(); - this->debounce_count = THEKERNEL->config->value(endstop_debounce_count_checksum )->by_default(100)->as_number(); + // NOTE the debouce count is in milliseconds so probably does not need to beset anymore + this->debounce_ms= THEKERNEL->config->value(endstop_debounce_ms_checksum )->by_default(0)->as_number(); + this->debounce_count= THEKERNEL->config->value(endstop_debounce_count_checksum )->by_default(100)->as_number(); // get homing direction and convert to boolean where true is home to min, and false is home to max - int home_dir = get_checksum(THEKERNEL->config->value(alpha_homing_direction_checksum)->by_default("home_to_min")->as_string()); - this->home_direction[0] = home_dir != home_to_max_checksum; + this->home_direction[0]= THEKERNEL->config->value(alpha_homing_direction_checksum)->by_default("home_to_min")->as_string() != "home_to_max"; + this->home_direction[1]= THEKERNEL->config->value(beta_homing_direction_checksum)->by_default("home_to_min")->as_string() != "home_to_max"; + this->home_direction[2]= THEKERNEL->config->value(gamma_homing_direction_checksum)->by_default("home_to_min")->as_string() != "home_to_max"; - home_dir = get_checksum(THEKERNEL->config->value(beta_homing_direction_checksum)->by_default("home_to_min")->as_string()); - this->home_direction[1] = home_dir != home_to_max_checksum; + this->homing_position[0]= this->home_direction[0] ? THEKERNEL->config->value(alpha_min_checksum)->by_default(0)->as_number() : THEKERNEL->config->value(alpha_max_checksum)->by_default(200)->as_number(); + this->homing_position[1]= this->home_direction[1] ? THEKERNEL->config->value(beta_min_checksum )->by_default(0)->as_number() : THEKERNEL->config->value(beta_max_checksum )->by_default(200)->as_number(); + this->homing_position[2]= this->home_direction[2] ? THEKERNEL->config->value(gamma_min_checksum)->by_default(0)->as_number() : THEKERNEL->config->value(gamma_max_checksum)->by_default(200)->as_number(); - home_dir = get_checksum(THEKERNEL->config->value(gamma_homing_direction_checksum)->by_default("home_to_min")->as_string()); - this->home_direction[2] = home_dir != home_to_max_checksum; + // used to set maximum movement on homing, set by alpha_max_travel if defined + // for backward compatibility uses alpha_max if not defined. + // TO BE deprecated + this->alpha_max= THEKERNEL->config->value(alpha_max_checksum)->by_default(500)->as_number(); + this->beta_max= THEKERNEL->config->value(beta_max_checksum)->by_default(500)->as_number(); + this->gamma_max= THEKERNEL->config->value(gamma_max_checksum)->by_default(500)->as_number(); - this->homing_position[0] = this->home_direction[0] ? THEKERNEL->config->value(alpha_min_checksum)->by_default(0)->as_number() : THEKERNEL->config->value(alpha_max_checksum)->by_default(200)->as_number(); - this->homing_position[1] = this->home_direction[1] ? THEKERNEL->config->value(beta_min_checksum )->by_default(0)->as_number() : THEKERNEL->config->value(beta_max_checksum )->by_default(200)->as_number(); - this->homing_position[2] = this->home_direction[2] ? THEKERNEL->config->value(gamma_min_checksum)->by_default(0)->as_number() : THEKERNEL->config->value(gamma_max_checksum)->by_default(200)->as_number(); + this->alpha_max= THEKERNEL->config->value(alpha_max_travel_checksum)->by_default(alpha_max*2)->as_number(); + this->beta_max= THEKERNEL->config->value(beta_max_travel_checksum)->by_default(beta_max*2)->as_number(); + this->gamma_max= THEKERNEL->config->value(gamma_max_travel_checksum)->by_default(gamma_max*2)->as_number(); this->is_corexy = THEKERNEL->config->value(corexy_homing_checksum)->by_default(false)->as_bool(); this->is_delta = THEKERNEL->config->value(delta_homing_checksum)->by_default(false)->as_bool(); this->is_rdelta = THEKERNEL->config->value(rdelta_homing_checksum)->by_default(false)->as_bool(); this->is_scara = THEKERNEL->config->value(scara_homing_checksum)->by_default(false)->as_bool(); + this->home_z_first = THEKERNEL->config->value(home_z_first_checksum)->by_default(false)->as_bool(); + // see if an order has been specified, must be three characters, XYZ or YXZ etc string order = THEKERNEL->config->value(homing_order_checksum)->by_default("")->as_string(); this->homing_order = 0; @@ -315,20 +330,20 @@ void Endstops::on_idle(void *argument) // if limit switches are enabled, then we must move off of the endstop otherwise we won't be able to move // checks if triggered and only backs off if triggered -void Endstops::back_off_home(char axes_to_move) +void Endstops::back_off_home(std::bitset<3> axis) { std::vector> params; this->status = BACK_OFF_HOME; // these are handled differently - if(this->is_delta || this->is_rdelta) { //TODO changed from is_delta + if(is_delta) { // Move off of the endstop using a regular relative move in Z only params.push_back({'Z', this->retract_mm[Z_AXIS] * (this->home_direction[Z_AXIS] ? 1 : -1)}); } else { // cartesians, concatenate all the moves we need to do into one gcode for( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if( ((axes_to_move >> c ) & 1) == 0) continue; // only for axes we asked to move + if(!axis[c]) continue; // only for axes we asked to move // if not triggered no need to move off if(this->limit_enable[c] && debounced_get(c + (this->home_direction[c] ? 0 : 3)) ) { @@ -345,286 +360,194 @@ void Endstops::back_off_home(char axes_to_move) char gcode_buf[64]; append_parameters(gcode_buf, params, sizeof(gcode_buf)); Gcode gc(gcode_buf, &(StreamOutput::NullStream)); - THEKERNEL->robot->push_state(); - THEKERNEL->robot->absolute_mode = false; // needs to be relative mode - THEKERNEL->robot->on_gcode_received(&gc); // send to robot directly + THEROBOT->push_state(); + THEROBOT->inch_mode = false; // needs to be in mm + THEROBOT->absolute_mode = false; // needs to be relative mode + THEROBOT->on_gcode_received(&gc); // send to robot directly // Wait for above to finish - THEKERNEL->conveyor->wait_for_empty_queue(); - THEKERNEL->robot->pop_state(); + THECONVEYOR->wait_for_idle(); + THEROBOT->pop_state(); } this->status = NOT_HOMING; } // If enabled will move the head to 0,0 after homing, but only if X and Y were set to home -void Endstops::move_to_origin(char axes_to_move) +void Endstops::move_to_origin(std::bitset<3> axis) { - if( (axes_to_move & 0x03) != 3 ) return; // ignore if X and Y not homing + if(!is_delta && (!axis[X_AXIS] || !axis[Y_AXIS])) return; // ignore if X and Y not homing, unless delta // Do we need to check if we are already at 0,0? probably not as the G0 will not do anything if we are - // float pos[3]; THEKERNEL->robot->get_axis_position(pos); if(pos[0] == 0 && pos[1] == 0) return; + // float pos[3]; THEROBOT->get_axis_position(pos); if(pos[0] == 0 && pos[1] == 0) return; this->status = MOVE_TO_ORIGIN; // Move to center using a regular move, use slower of X and Y fast rate float rate = std::min(this->fast_rates[0], this->fast_rates[1]) * 60.0F; char buf[32]; + THEROBOT->push_state(); + THEROBOT->inch_mode = false; // needs to be in mm + THEROBOT->absolute_mode = true; snprintf(buf, sizeof(buf), "G53 G0 X0 Y0 F%1.4f", rate); // must use machine coordinates in case G92 or WCS is in effect - THEKERNEL->robot->push_state(); struct SerialMessage message; message.message = buf; message.stream = &(StreamOutput::NullStream); THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message ); // as it is a multi G code command // Wait for above to finish - THEKERNEL->conveyor->wait_for_empty_queue(); - THEKERNEL->robot->pop_state(); + THECONVEYOR->wait_for_idle(); + THEROBOT->pop_state(); this->status = NOT_HOMING; } -bool Endstops::wait_for_homed(char axes_to_move) +// Called every millisecond in an ISR +uint32_t Endstops::read_endstops(uint32_t dummy) { - bool running = true; - unsigned int debounce[3] = {0, 0, 0}; - while (running) { - SEGGER_SYSVIEW_Print("Endstop::wait_for_homed\n"); - running = false; - THEKERNEL->call_event(ON_IDLE); - - // check if on_halt (eg kill) - if(THEKERNEL->is_halted()) return false; + if(this->status != MOVING_TO_ENDSTOP_SLOW && this->status != MOVING_TO_ENDSTOP_FAST) return 0; // not doing anything we need to monitor for - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if ( ( axes_to_move >> c ) & 1 ) { - //SEGGER_SYSVIEW_PrintfHost(" debounce[%u] = %u\n",c,debounce[c]); - if ( this->pins[c + (this->home_direction[c] ? 0 : 3)].get() ) { - //SEGGER_SYSVIEW_PrintfHost("A debounce[%u] = %u\n",c,debounce[c]); - if ( debounce[c] < debounce_count ) { - debounce[c]++; - running = true; - } else if ( STEPPER[c]->is_moving() ) { - SEGGER_SYSVIEW_PrintfHost("Reached endstop for %u\n",c); - //STEPPER[c]->move(0, 0); - STEPPER[c]->force_finish_move(); //TODO test - SEGGER_SYSVIEW_PrintfHost("Axis to move a %u\n",axes_to_move); - axes_to_move &= ~(1 << c); // no need to check it again - SEGGER_SYSVIEW_PrintfHost("Axis to move %u\n",axes_to_move); + if(!is_corexy) { + // check each axis + for ( int m = X_AXIS; m <= Z_AXIS; m++ ) { + if(STEPPER[m]->is_moving()) { + // if it is moving then we check the associated endstop, and debounce it + if(this->pins[m + (this->home_direction[m] ? 0 : 3)].get()) { + if(debounce[m] < debounce_ms) { + debounce[m]++; + } else { + // we signal the motor to stop, which will preempt any moves on that axis + STEPPER[m]->stop_moving(); } } else { // The endstop was not hit yet - //SEGGER_SYSVIEW_PrintfHost("C debounce[%u] = %u\n",c,debounce[c]); - running = true; - debounce[c] = 0; + debounce[m] = 0; } } } - SEGGER_SYSVIEW_PrintfHost("%u,%u,%u\n",debounce[0],debounce[1],debounce[2]); - } - SEGGER_SYSVIEW_Print("Endstop::wait_for_homed EXIT\n"); - return true; -} -void Endstops::do_homing_cartesian(char axes_to_move) -{ - // check if on_halt (eg kill) - if(THEKERNEL->is_halted()) return; - - // this homing works for cartesian and delta printers - // Start moving the axes to the origin - this->status = MOVING_TO_ENDSTOP_FAST; - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if ( ( axes_to_move >> c) & 1 ) { - this->feed_rate[c] = this->fast_rates[c]; - STEPPER[c]->move(this->home_direction[c], 10000000, 0); - } - } - - // Wait for all axes to have homed - if(!this->wait_for_homed(axes_to_move)) return; - - // Move back a small distance - this->status = MOVING_BACK; - bool inverted_dir; - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if ( ( axes_to_move >> c ) & 1 ) { - inverted_dir = !this->home_direction[c]; - this->feed_rate[c] = this->slow_rates[c]; - STEPPER[c]->move(inverted_dir, this->retract_mm[c]*STEPS_PER_MM(c), 0); - } - } + } else { + // corexy is different as the actuators are not directly related to the XY axis + // so we check the axis that is currently homing then stop all motors + for ( int m = X_AXIS; m <= Z_AXIS; m++ ) { + if(axis_to_home[m]) { + if(this->pins[m + (this->home_direction[m] ? 0 : 3)].get()) { + if(debounce[m] < debounce_ms) { + debounce[m]++; + } else { + // we signal all the motors to stop, as on corexy X and Y motors will move for X and Y axis homing and we only hom eone axis at a time + STEPPER[X_AXIS]->stop_moving(); + STEPPER[Y_AXIS]->stop_moving(); + STEPPER[Z_AXIS]->stop_moving(); + } - // Wait for moves to be done - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if ( ( axes_to_move >> c ) & 1 ) { - while ( STEPPER[c]->is_moving() ) { - THEKERNEL->call_event(ON_IDLE); - if(THEKERNEL->is_halted()) return; + } else { + // The endstop was not hit yet + debounce[m] = 0; + } } } } - // Start moving the axes to the origin slowly - this->status = MOVING_TO_ENDSTOP_SLOW; - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if ( ( axes_to_move >> c ) & 1 ) { - this->feed_rate[c] = this->slow_rates[c]; - STEPPER[c]->move(this->home_direction[c], 10000000, 0); - } - } - - // Wait for all axes to have homed - if(!this->wait_for_homed(axes_to_move)) return; + return 0; } -bool Endstops::wait_for_homed_corexy(int axis) +void Endstops::home_xy() { - bool running = true; - unsigned int debounce[3] = {0, 0, 0}; - while (running) { - running = false; - THEKERNEL->call_event(ON_IDLE); - - // check if on_halt (eg kill) - if(THEKERNEL->is_halted()) return false; - - if ( this->pins[axis + (this->home_direction[axis] ? 0 : 3)].get() ) { - if ( debounce[axis] < debounce_count ) { - debounce[axis] ++; - running = true; - } else { - // turn both off if running - if (STEPPER[X_AXIS]->is_moving()) STEPPER[X_AXIS]->move(0, 0); - if (STEPPER[Y_AXIS]->is_moving()) STEPPER[Y_AXIS]->move(0, 0); - } - } else { - // The endstop was not hit yet - running = true; - debounce[axis] = 0; - } - } - return true; + if(axis_to_home[X_AXIS] && axis_to_home[Y_AXIS]) { + // Home XY first so as not to slow them down by homing Z at the same time + float delta[3] {alpha_max, beta_max, 0}; + if(this->home_direction[X_AXIS]) delta[X_AXIS]= -delta[X_AXIS]; + if(this->home_direction[Y_AXIS]) delta[Y_AXIS]= -delta[Y_AXIS]; + float feed_rate = std::min(fast_rates[X_AXIS], fast_rates[Y_AXIS]); + THEROBOT->delta_move(delta, feed_rate, 3); + + } else if(axis_to_home[X_AXIS]) { + // now home X only + float delta[3] {alpha_max, 0, 0}; + if(this->home_direction[X_AXIS]) delta[X_AXIS]= -delta[X_AXIS]; + THEROBOT->delta_move(delta, fast_rates[X_AXIS], 3); + + } else if(axis_to_home[Y_AXIS]) { + // now home Y only + float delta[3] {0, beta_max, 0}; + if(this->home_direction[Y_AXIS]) delta[Y_AXIS]= -delta[Y_AXIS]; + THEROBOT->delta_move(delta, fast_rates[Y_AXIS], 3); + } + + // Wait for axis to have homed + THECONVEYOR->wait_for_idle(); } -void Endstops::corexy_home(int home_axis, bool dirx, bool diry, float fast_rate, float slow_rate, unsigned int retract_steps) +void Endstops::home(std::bitset<3> a) { - // check if on_halt (eg kill) - if(THEKERNEL->is_halted()) return; + // reset debounce counts + debounce.fill(0); + + // turn off any compensation transform + auto savect= THEROBOT->compensationTransform; + THEROBOT->compensationTransform= nullptr; + this->axis_to_home= a; + + // Start moving the axes to the origin this->status = MOVING_TO_ENDSTOP_FAST; - this->feed_rate[X_AXIS] = fast_rate; - STEPPER[X_AXIS]->move(dirx, 10000000, 0); - this->feed_rate[Y_AXIS] = fast_rate; - STEPPER[Y_AXIS]->move(diry, 10000000, 0); - // wait for primary axis - if(!this->wait_for_homed_corexy(home_axis)) return; + THEROBOT->disable_segmentation= true; // we must disable segmentation as this won't work with it enabled - // Move back a small distance - this->status = MOVING_BACK; - this->feed_rate[X_AXIS] = slow_rate; - STEPPER[X_AXIS]->move(!dirx, retract_steps, 0); - this->feed_rate[Y_AXIS] = slow_rate; - STEPPER[Y_AXIS]->move(!diry, retract_steps, 0); - - // wait until done - while ( STEPPER[X_AXIS]->is_moving() || STEPPER[Y_AXIS]->is_moving()) { - THEKERNEL->call_event(ON_IDLE); - if(THEKERNEL->is_halted()) return; + if(!home_z_first) home_xy(); + + if(axis_to_home[Z_AXIS]) { + // now home z + float delta[3] {0, 0, gamma_max}; // we go the max z + if(this->home_direction[Z_AXIS]) delta[Z_AXIS]= -delta[Z_AXIS]; + THEROBOT->delta_move(delta, fast_rates[Z_AXIS], 3); + // wait for Z + THECONVEYOR->wait_for_idle(); } - // Start moving the axes to the origin slowly - this->status = MOVING_TO_ENDSTOP_SLOW; - this->feed_rate[X_AXIS] = slow_rate; - STEPPER[X_AXIS]->move(dirx, 10000000, 0); - this->feed_rate[Y_AXIS] = slow_rate; - STEPPER[Y_AXIS]->move(diry, 10000000, 0); + if(home_z_first) home_xy(); - // wait for primary axis - if(!this->wait_for_homed_corexy(home_axis)) return; -} + // TODO should check that the endstops were hit and it did not stop short for some reason + // we did not complete movement the full distance if we hit the endstops + THEROBOT->reset_position_from_current_actuator_position(); -// this homing works for HBots/CoreXY -void Endstops::do_homing_corexy(char axes_to_move) -{ - // TODO should really make order configurable, and select whether to allow XY to home at the same time, diagonally - // To move XY at the same time only one motor needs to turn, determine which motor and which direction based on min or max directions - // allow to move until an endstop triggers, then stop that motor. Speed up when moving diagonally to match X or Y speed - // continue moving in the direction not yet triggered (which means two motors turning) until endstop hit - - if((axes_to_move & 0x03) == 0x03) { // both X and Y need Homing - // determine which motor to turn and which way - bool dirx = this->home_direction[X_AXIS]; - bool diry = this->home_direction[Y_AXIS]; - int motor; - bool dir; - if(dirx && diry) { // min/min - motor = X_AXIS; - dir = true; - } else if(dirx && !diry) { // min/max - motor = Y_AXIS; - dir = true; - } else if(!dirx && diry) { // max/min - motor = Y_AXIS; - dir = false; - } else if(!dirx && !diry) { // max/max - motor = X_AXIS; - dir = false; - } - - // then move both X and Y until one hits the endstop - this->status = MOVING_TO_ENDSTOP_FAST; - // need to allow for more ground covered when moving diagonally - this->feed_rate[motor] = this->fast_rates[motor] * 1.4142; - STEPPER[motor]->move(dir, 10000000, 0); - // wait until either X or Y hits the endstop - bool running = true; - while (running) { - THEKERNEL->call_event(ON_IDLE); - if(THEKERNEL->is_halted()) return; - for(int m = X_AXIS; m <= Y_AXIS; m++) { - if(this->pins[m + (this->home_direction[m] ? 0 : 3)].get()) { - // turn off motor - if(STEPPER[motor]->is_moving()) STEPPER[motor]->move(0, 0); - running = false; - break; - } - } + // Move back a small distance for all homing axis + this->status = MOVING_BACK; + float delta[3]{0,0,0}; + // use minimum feed rate of all three axes that are being homed (sub optimal, but necessary) + float feed_rate= slow_rates[X_AXIS]; + for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { + if(axis_to_home[c]) { + delta[c]= this->retract_mm[c]; + if(!this->home_direction[c]) delta[c]= -delta[c]; + feed_rate= std::min(slow_rates[c], feed_rate); } } - // move individual axis - if (axes_to_move & 0x01) { // Home X, which means both X and Y in same direction - bool dir = this->home_direction[X_AXIS]; - corexy_home(X_AXIS, dir, dir, this->fast_rates[X_AXIS], this->slow_rates[X_AXIS], this->retract_mm[X_AXIS]*STEPS_PER_MM(X_AXIS)); - } + THEROBOT->delta_move(delta, feed_rate, 3); + // wait until finished + THECONVEYOR->wait_for_idle(); - if (axes_to_move & 0x02) { // Home Y, which means both X and Y in different directions - bool dir = this->home_direction[Y_AXIS]; - corexy_home(Y_AXIS, dir, !dir, this->fast_rates[Y_AXIS], this->slow_rates[Y_AXIS], this->retract_mm[Y_AXIS]*STEPS_PER_MM(Y_AXIS)); + // Start moving the axes towards the endstops slowly + this->status = MOVING_TO_ENDSTOP_SLOW; + for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { + if(axis_to_home[c]) { + delta[c]= this->retract_mm[c]*2; // move further than we moved off to make sure we hit it cleanly + if(this->home_direction[c]) delta[c]= -delta[c]; + }else{ + delta[c]= 0; + } } + THEROBOT->delta_move(delta, feed_rate, 3); + // wait until finished + THECONVEYOR->wait_for_idle(); - if (axes_to_move & 0x04) { // move Z - do_homing_cartesian(0x04); // just home normally for Z - } -} + // TODO should check that the endstops were hit and it did not stop short for some reason + // we did not complete movement the full distance if we hit the endstops + THEROBOT->reset_position_from_current_actuator_position(); -void Endstops::home(char axes_to_move) -{ - // not a block move so disable the last tick setting - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - STEPPER[c]->set_moved_last_block(false); - } + THEROBOT->disable_segmentation= false; - if (is_corexy) { - // corexy/HBot homing - do_homing_corexy(axes_to_move); - } else { - // cartesian/delta homing - do_homing_cartesian(axes_to_move); - } + // restore compensationTransform + THEROBOT->compensationTransform= savect; - // make sure all steppers are off (especially if aborted) - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - STEPPER[c]->move(0, 0); - } this->status = NOT_HOMING; } @@ -633,12 +556,18 @@ void Endstops::process_home_command(Gcode* gcode) if( (gcode->subcode == 0 && THEKERNEL->is_grbl_mode()) || (gcode->subcode == 2 && !THEKERNEL->is_grbl_mode()) ) { // G28 in grbl mode or G28.2 in normal mode will do a rapid to the predefined position // TODO spec says if XYZ specified move to them first then move to MCS of specifed axis + THEROBOT->push_state(); + THEROBOT->inch_mode = false; // needs to be in mm + THEROBOT->absolute_mode = true; char buf[32]; snprintf(buf, sizeof(buf), "G53 G0 X%f Y%f", saved_position[X_AXIS], saved_position[Y_AXIS]); // must use machine coordinates in case G92 or WCS is in effect struct SerialMessage message; message.message = buf; message.stream = &(StreamOutput::NullStream); THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message ); // as it is a multi G code command + // Wait for above to finish + THECONVEYOR->wait_for_idle(); + THEROBOT->pop_state(); return; } else if(THEKERNEL->is_grbl_mode() && gcode->subcode == 2) { // G28.2 in grbl mode forces homing (triggered by $H) @@ -646,27 +575,31 @@ void Endstops::process_home_command(Gcode* gcode) } else if(gcode->subcode == 1) { // G28.1 set pre defined position // saves current position in absolute machine coordinates - THEKERNEL->robot->get_axis_position(saved_position); + THEROBOT->get_axis_position(saved_position); // Only XY are used + // Note the following is only meant to be used for recovering a saved position from config-override + // Not a standard Gcode and not to be relied on + if (gcode->has_letter('X')) saved_position[X_AXIS] = gcode->get_value('X'); + if (gcode->has_letter('Y')) saved_position[Y_AXIS] = gcode->get_value('Y'); return; } else if(gcode->subcode == 3) { // G28.3 is a smoothie special it sets manual homing if(gcode->get_num_args() == 0) { - THEKERNEL->robot->reset_axis_position(0, 0, 0); + THEROBOT->reset_axis_position(0, 0, 0); } else { // do a manual homing based on given coordinates, no endstops required - if(gcode->has_letter('X')) THEKERNEL->robot->reset_axis_position(gcode->get_value('X'), X_AXIS); - if(gcode->has_letter('Y')) THEKERNEL->robot->reset_axis_position(gcode->get_value('Y'), Y_AXIS); - if(gcode->has_letter('Z')) THEKERNEL->robot->reset_axis_position(gcode->get_value('Z'), Z_AXIS); + if(gcode->has_letter('X')) THEROBOT->reset_axis_position(gcode->get_value('X'), X_AXIS); + if(gcode->has_letter('Y')) THEROBOT->reset_axis_position(gcode->get_value('Y'), Y_AXIS); + if(gcode->has_letter('Z')) THEROBOT->reset_axis_position(gcode->get_value('Z'), Z_AXIS); } return; } else if(gcode->subcode == 4) { // G28.4 is a smoothie special it sets manual homing based on the actuator position (used for rotary delta) - // do a manual homing based on given coordinates, no endstops required, NOTE does not support the multi actuator hack + // do a manual homing based on given coordinates, no endstops required ActuatorCoordinates ac; - if(gcode->has_letter('A')) ac[0] = gcode->get_value('A'); - if(gcode->has_letter('B')) ac[1] = gcode->get_value('B'); - if(gcode->has_letter('C')) ac[2] = gcode->get_value('C'); - THEKERNEL->robot->reset_actuator_position(ac); + if(gcode->has_letter('X')) ac[0] = gcode->get_value('X'); + if(gcode->has_letter('Y')) ac[1] = gcode->get_value('Y'); + if(gcode->has_letter('Z')) ac[2] = gcode->get_value('Z'); + THEROBOT->reset_actuator_position(ac); return; } else if(THEKERNEL->is_grbl_mode()) { @@ -677,55 +610,52 @@ void Endstops::process_home_command(Gcode* gcode) // G28 is received, we have homing to do // First wait for the queue to be empty - THEKERNEL->conveyor->wait_for_empty_queue(); + THECONVEYOR->wait_for_idle(); - // Do we move select axes or all of them - char axes_to_move = 0; + // deltas always home Z axis only, which moves all three actuators + bool home_in_z = this->is_delta || this->is_rdelta; - // deltas, scaras always home all axis - bool home_all = this->is_delta || this->is_rdelta || this->is_scara; + // figure out which axis to home + bitset<3> haxis; + haxis.reset(); - if(!home_all) { // ie not a delta - bool axis_speced= ( gcode->has_letter('X') || gcode->has_letter('Y') || gcode->has_letter('Z') ); + if(!home_in_z) { // ie not a delta + bool axis_speced = ( gcode->has_letter('X') || gcode->has_letter('Y') || gcode->has_letter('Z') ); // only enable homing if the endstop is defined, for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { if (this->pins[c + (this->home_direction[c] ? 0 : 3)].connected() && (!axis_speced || gcode->has_letter(c + 'X')) ) { - axes_to_move += ( 1 << c ); + haxis.set(c); + // now reset axis to 0 as we do not know what state we are in + THEROBOT->reset_axis_position(0, c); } } - }else{ - // all axis must move (and presumed defined) - axes_to_move= 7; + } else { + // Only Z axis homes (even though all actuators move this is handled by arm solution) + haxis.set(Z_AXIS); + // we also set the kinematics to a known good position, this is necessary for a rotary delta, but doesn't hurt for linear delta + THEROBOT->reset_axis_position(0, 0, 0); } - // save current actuator position so we can report how far we moved - ActuatorCoordinates start_pos{ - THEKERNEL->robot->actuators[X_AXIS]->get_current_position(), - THEKERNEL->robot->actuators[Y_AXIS]->get_current_position(), - THEKERNEL->robot->actuators[Z_AXIS]->get_current_position() - }; - - // Enable the motors - THEKERNEL->stepper->turn_enable_pins_on(); - // do the actual homing if(homing_order != 0) { // if an order has been specified do it in the specified order // homing order is 0b00ccbbaa where aa is 0,1,2 to specify the first axis, bb is the second and cc is the third // eg 0b00100001 would be Y X Z, 0b00100100 would be X Y Z for (uint8_t m = homing_order; m != 0; m >>= 2) { - int a = (1 << (m & 0x03)); // axis to move - if((a & axes_to_move) != 0) { - home(a); + int a= (m & 0x03); // axis to home + if(haxis[a]) { // if axis is selected to home + std::bitset<3> bs; + bs.set(a); + home(bs); } // check if on_halt (eg kill) if(THEKERNEL->is_halted()) break; } } else { - // they all home at the same time - home(axes_to_move); + // they could all home at the same time + home(haxis); } // check if on_halt (eg kill) @@ -736,15 +666,7 @@ void Endstops::process_home_command(Gcode* gcode) return; } - // set the last probe position to the actuator units moved during this home - THEKERNEL->robot->set_last_probe_position( - std::make_tuple( - start_pos[0] - THEKERNEL->robot->actuators[0]->get_current_position(), - start_pos[1] - THEKERNEL->robot->actuators[1]->get_current_position(), - start_pos[2] - THEKERNEL->robot->actuators[2]->get_current_position(), - 0)); - - if(home_all) { + if(home_in_z) { // deltas only // Here's where we would have been if the endstops were perfectly trimmed // NOTE on a rotary delta home_offset is actuator position in degrees when homed and // home_offset is the theta offset for each actuator, so M206 is used to set theta offset for each actuator in degrees @@ -754,10 +676,10 @@ void Endstops::process_home_command(Gcode* gcode) this->homing_position[Z_AXIS] + this->home_offset[Z_AXIS] }; - bool has_endstop_trim = this->is_delta || this->is_scara; + bool has_endstop_trim = this->is_delta; if (has_endstop_trim) { ActuatorCoordinates ideal_actuator_position; - THEKERNEL->robot->arm_solution->cartesian_to_actuator(ideal_position, ideal_actuator_position); + THEROBOT->arm_solution->cartesian_to_actuator(ideal_position, ideal_actuator_position); // We are actually not at the ideal position, but a trim away ActuatorCoordinates real_actuator_position = { @@ -767,28 +689,28 @@ void Endstops::process_home_command(Gcode* gcode) }; float real_position[3]; - THEKERNEL->robot->arm_solution->actuator_to_cartesian(real_actuator_position, real_position); + THEROBOT->arm_solution->actuator_to_cartesian(real_actuator_position, real_position); // Reset the actuator positions to correspond our real position - THEKERNEL->robot->reset_axis_position(real_position[0], real_position[1], real_position[2]); + THEROBOT->reset_axis_position(real_position[0], real_position[1], real_position[2]); } else { // without endstop trim, real_position == ideal_position if(is_rdelta) { // with a rotary delta we set the actuators angle then use the FK to calculate the resulting cartesian coordinates ActuatorCoordinates real_actuator_position = {ideal_position[0], ideal_position[1], ideal_position[2]}; - THEKERNEL->robot->reset_actuator_position(real_actuator_position); + THEROBOT->reset_actuator_position(real_actuator_position); } else { // Reset the actuator positions to correspond our real position - THEKERNEL->robot->reset_axis_position(ideal_position[0], ideal_position[1], ideal_position[2]); + THEROBOT->reset_axis_position(ideal_position[0], ideal_position[1], ideal_position[2]); } } } else { // Zero the ax(i/e)s position, add in the home offset for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if ( (axes_to_move >> c) & 1 ) { - THEKERNEL->robot->reset_axis_position(this->homing_position[c] + this->home_offset[c], c); + if (haxis[c]) { // if we requested this axis to home + THEROBOT->reset_axis_position(this->homing_position[c] + this->home_offset[c], c); } } } @@ -797,15 +719,15 @@ void Endstops::process_home_command(Gcode* gcode) // default is off for cartesian on for deltas if(!is_delta) { // NOTE a rotary delta usually has optical or hall-effect endstops so it is safe to go past them a little bit - if(this->move_to_origin_after_home) move_to_origin(axes_to_move); + if(this->move_to_origin_after_home) move_to_origin(haxis); // if limit switches are enabled we must back off endstop after setting home - back_off_home(axes_to_move); + back_off_home(haxis); } else if(this->move_to_origin_after_home || this->limit_enable[X_AXIS]) { // deltas are not left at 0,0 because of the trim settings, so move to 0,0 if requested, but we need to back off endstops first // also need to back off endstops if limits are enabled - back_off_home(axes_to_move); - if(this->move_to_origin_after_home) move_to_origin(axes_to_move); + back_off_home(haxis); + if(this->move_to_origin_after_home) move_to_origin(haxis); } } @@ -813,18 +735,18 @@ void Endstops::set_homing_offset(Gcode *gcode) { // Similar to M206 and G92 but sets Homing offsets based on current position float cartesian[3]; - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEROBOT->get_axis_position(cartesian); // get actual position from robot if (gcode->has_letter('X')) { home_offset[0] -= (cartesian[X_AXIS] - gcode->get_value('X')); - THEKERNEL->robot->reset_axis_position(gcode->get_value('X'), X_AXIS); + THEROBOT->reset_axis_position(gcode->get_value('X'), X_AXIS); } if (gcode->has_letter('Y')) { home_offset[1] -= (cartesian[Y_AXIS] - gcode->get_value('Y')); - THEKERNEL->robot->reset_axis_position(gcode->get_value('Y'), Y_AXIS); + THEROBOT->reset_axis_position(gcode->get_value('Y'), Y_AXIS); } if (gcode->has_letter('Z')) { home_offset[2] -= (cartesian[Z_AXIS] - gcode->get_value('Z')); - THEKERNEL->robot->reset_axis_position(gcode->get_value('Z'), Z_AXIS); + THEROBOT->reset_axis_position(gcode->get_value('Z'), Z_AXIS); } gcode->stream->printf("Homing Offset: X %5.3f Y %5.3f Z %5.3f\n", home_offset[0], home_offset[1], home_offset[2]); @@ -877,7 +799,7 @@ void Endstops::on_gcode_received(void *argument) gcode->stream->printf(";Max Z\nM665 Z%1.3f\n", this->homing_position[2]); } if(saved_position[X_AXIS] != 0 || saved_position[Y_AXIS] != 0) { - gcode->stream->printf(";predefined position:\nG28.1 X%1.4f Y%1.4f Z%1.4f\n", saved_position[X_AXIS], saved_position[Y_AXIS], saved_position[Z_AXIS]); + gcode->stream->printf(";predefined position:\nG28.1 X%1.4f Y%1.4f\n", saved_position[X_AXIS], saved_position[Y_AXIS]); } break; @@ -903,7 +825,8 @@ void Endstops::on_gcode_received(void *argument) } break; - +//TODO ADDBACK +/* // NOTE this is to test accuracy of lead screws etc. case 1910: { // M1910.0 - move specific number of raw steps @@ -944,35 +867,11 @@ void Endstops::on_gcode_received(void *argument) } break; } +*/ } } } -// Called periodically to change the speed to match acceleration -void Endstops::acceleration_tick(void) -{ - if(this->status >= NOT_HOMING) return; // nothing to do, only do this when moving for homing sequence - - // foreach stepper that is moving - for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { - if( !STEPPER[c]->is_moving() ) continue; - - uint32_t current_rate = STEPPER[c]->get_steps_per_second(); - uint32_t target_rate = floorf(this->feed_rate[c] * STEPS_PER_MM(c)); - float acc = (c == Z_AXIS) ? THEKERNEL->planner->get_z_acceleration() : THEKERNEL->planner->get_acceleration(); - if( current_rate < target_rate ) { - uint32_t rate_increase = floorf((acc / THEKERNEL->acceleration_ticks_per_second) * STEPS_PER_MM(c)); - current_rate = min( target_rate, current_rate + rate_increase ); - } - if( current_rate > target_rate ) { current_rate = target_rate; } - - // steps per second - STEPPER[c]->set_speed(current_rate); - } - - return; -} - void Endstops::on_get_public_data(void* argument) { PublicDataRequest* pdr = static_cast(argument); diff --git a/src/modules/tools/endstops/Endstops.h b/src/modules/tools/endstops/Endstops.h index 96173c44..387781be 100644 --- a/src/modules/tools/endstops/Endstops.h +++ b/src/modules/tools/endstops/Endstops.h @@ -5,13 +5,13 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef ENDSTOPS_MODULE_H -#define ENDSTOPS_MODULE_H +#pragma once #include "libs/Module.h" #include "libs/Pin.h" #include +#include class StepperMotor; class Gcode; @@ -21,48 +21,48 @@ class Endstops : public Module{ Endstops(); void on_module_loaded(); void on_gcode_received(void* argument); - void acceleration_tick(void); private: void load_config(); - void home(char axes_to_move); - void do_homing_cartesian(char axes_to_move); - void do_homing_corexy(char axes_to_move); - bool wait_for_homed(char axes_to_move); - bool wait_for_homed_corexy(int axis); - void corexy_home(int home_axis, bool dirx, bool diry, float fast_rate, float slow_rate, unsigned int retract_steps); - void back_off_home(char axes_to_move); - void move_to_origin(char); + void home(std::bitset<3> a); + void home_xy(); + void back_off_home(std::bitset<3> axis); + void move_to_origin(std::bitset<3> axis); void on_get_public_data(void* argument); void on_set_public_data(void* argument); void on_idle(void *argument); bool debounced_get(int pin); void process_home_command(Gcode* gcode); void set_homing_offset(Gcode* gcode); + uint32_t read_endstops(uint32_t dummy); float homing_position[3]; float home_offset[3]; - uint8_t homing_order; - std::bitset<3> home_direction; - std::bitset<3> limit_enable; float saved_position[3]{0}; // save G28 (in grbl mode) + float alpha_max, beta_max, gamma_max; - unsigned int debounce_count; + uint32_t debounce_count; + uint32_t debounce_ms; float retract_mm[3]; float trim_mm[3]; float fast_rates[3]; float slow_rates[3]; Pin pins[6]; - volatile float feed_rate[3]; + std::array debounce; + + std::bitset<3> home_direction; + std::bitset<3> limit_enable; + std::bitset<3> axis_to_home; + struct { + uint8_t homing_order:6; + uint8_t bounce_cnt:4; + volatile char status:3; bool is_corexy:1; bool is_delta:1; bool is_rdelta:1; bool is_scara:1; + bool home_z_first:1; bool move_to_origin_after_home:1; - uint8_t bounce_cnt:4; - volatile char status:3; }; }; - -#endif diff --git a/src/modules/tools/filamentdetector/FilamentDetector.cpp b/src/modules/tools/filamentdetector/FilamentDetector.cpp index 327443e4..217ccf26 100644 --- a/src/modules/tools/filamentdetector/FilamentDetector.cpp +++ b/src/modules/tools/filamentdetector/FilamentDetector.cpp @@ -74,11 +74,12 @@ void FilamentDetector::on_module_loaded() return; } - //only monitor the encoder if we are using the encodeer. + //only monitor the encoder if we are using the encoder. if (this->encoder_pin != nullptr) { // set interrupt on rising edge this->encoder_pin->rise(this, &FilamentDetector::on_pin_rise); - NVIC_SetPriority(EINT3_IRQn, 16); // set to low priority + //NVIC_SetPriority(EINT3_IRQn, 16); // set to low priority +//TODO We need to fix the interrupts, something like the following NVIC_SetPriority(PORTA_IRQn, 0x1); } diff --git a/src/modules/tools/laser/Laser.cpp b/src/modules/tools/laser/Laser.cpp index 3f544fb3..796338fb 100644 --- a/src/modules/tools/laser/Laser.cpp +++ b/src/modules/tools/laser/Laser.cpp @@ -5,36 +5,45 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#include "libs/Module.h" -#include "libs/Kernel.h" -#include "modules/communication/utils/Gcode.h" -#include "modules/robot/Stepper.h" #include "Laser.h" -#include "libs/nuts_bolts.h" +#include "Module.h" +#include "Kernel.h" +#include "nuts_bolts.h" #include "Config.h" #include "StreamOutputPool.h" -#include "Block.h" +#include "SerialMessage.h" #include "checksumm.h" #include "ConfigValue.h" - -#include "libs/Pin.h" +#include "StepTicker.h" +#include "Block.h" +#include "SlowTicker.h" +#include "Robot.h" +#include "utils.h" +#include "Pin.h" #include "Gcode.h" #include "PwmOut.h" // mbed.h lib +#include "PublicDataRequest.h" -#define laser_module_enable_checksum CHECKSUM("laser_module_enable") -#define laser_module_pin_checksum CHECKSUM("laser_module_pin") -#define laser_module_pwm_pin_checksum CHECKSUM("laser_module_pwm_pin") -#define laser_module_ttl_pin_checksum CHECKSUM("laser_module_ttl_pin") -#define laser_module_pwm_period_checksum CHECKSUM("laser_module_pwm_period") -#define laser_module_maximum_power_checksum CHECKSUM("laser_module_maximum_power") +#include + +#define laser_checksum CHECKSUM("laser") +#define laser_module_enable_checksum CHECKSUM("laser_module_enable") +#define laser_module_pin_checksum CHECKSUM("laser_module_pin") +#define laser_module_pwm_pin_checksum CHECKSUM("laser_module_pwm_pin") +#define laser_module_ttl_pin_checksum CHECKSUM("laser_module_ttl_pin") +#define laser_module_pwm_period_checksum CHECKSUM("laser_module_pwm_period") +#define laser_module_maximum_power_checksum CHECKSUM("laser_module_maximum_power") #define laser_module_minimum_power_checksum CHECKSUM("laser_module_minimum_power") -#define laser_module_default_power_checksum CHECKSUM("laser_module_default_power") #define laser_module_tickle_power_checksum CHECKSUM("laser_module_tickle_power") #define laser_module_max_power_checksum CHECKSUM("laser_module_max_power") #define laser_module_maximum_s_value_checksum CHECKSUM("laser_module_maximum_s_value") -Laser::Laser(){ +Laser::Laser() +{ + laser_on = false; + scale= 1; + manual_fire= false; } void Laser::on_module_loaded() { @@ -54,7 +63,7 @@ void Laser::on_module_loaded() { pwm_pin = dummy_pin->hardware_pwm(); - if (pwm_pin == NULL) + if (pwm_pin == nullptr) { // TOADDBACK THEKERNEL->streams->printf("Error: Laser cannot use P%d.%d (P2.0 - P2.5, P1.18, P1.20, P1.21, P1.23, P1.24, P1.26, P3.25, P3.26 only). Laser module disabled.\n", dummy_pin->port_number, dummy_pin->pin); delete dummy_pin; @@ -74,103 +83,194 @@ void Laser::on_module_loaded() { this->ttl_used = ttl_pin->connected(); this->ttl_inverting = ttl_pin->is_inverting(); if (ttl_used) { - ttl_pin->set(0); + ttl_pin->set(0); } else { - delete ttl_pin; - ttl_pin = NULL; + delete ttl_pin; + ttl_pin = NULL; } - this->pwm_pin->period_us(THEKERNEL->config->value(laser_module_pwm_period_checksum)->by_default(20)->as_number()); + uint32_t period= THEKERNEL->config->value(laser_module_pwm_period_checksum)->by_default(20)->as_number(); + this->pwm_pin->period_us(period); this->pwm_pin->write(this->pwm_inverting ? 1 : 0); this->laser_maximum_power = THEKERNEL->config->value(laser_module_maximum_power_checksum)->by_default(1.0f)->as_number() ; // These config variables are deprecated, they have been replaced with laser_module_default_power and laser_module_minimum_power this->laser_minimum_power = THEKERNEL->config->value(laser_module_tickle_power_checksum)->by_default(0)->as_number() ; - this->laser_power = THEKERNEL->config->value(laser_module_max_power_checksum)->by_default(0.8f)->as_number() ; // Load in our preferred config variables this->laser_minimum_power = THEKERNEL->config->value(laser_module_minimum_power_checksum)->by_default(this->laser_minimum_power)->as_number() ; - this->laser_power = THEKERNEL->config->value(laser_module_default_power_checksum)->by_default(this->laser_power)->as_number() ; // S value that represents maximum (default 1) this->laser_maximum_s_value = THEKERNEL->config->value(laser_module_maximum_s_value_checksum)->by_default(1.0f)->as_number() ; + set_laser_power(0); + //register for events this->register_for_event(ON_GCODE_EXECUTE); this->register_for_event(ON_SPEED_CHANGE); this->register_for_event(ON_BLOCK_BEGIN); this->register_for_event(ON_BLOCK_END); this->register_for_event(ON_HALT); + this->register_for_event(ON_GCODE_RECEIVED); + this->register_for_event(ON_CONSOLE_LINE_RECEIVED); + this->register_for_event(ON_GET_PUBLIC_DATA); + + // no point in updating the power more than the PWM frequency, but no more than 1KHz + THEKERNEL->slow_ticker->attach(std::min(1000UL, 1000000/period), this, &Laser::set_proportional_power); } -// Turn laser off laser at the end of a move -void Laser::on_block_end(void* argument){ - this->pwm_pin->write(this->pwm_inverting ? 1 : 0); +void Laser::on_console_line_received( void *argument ) +{ + if(THEKERNEL->is_halted()) return; // if in halted state ignore any commands + + SerialMessage *msgp = static_cast(argument); + string possible_command = msgp->message; + + // ignore anything that is not lowercase or a letter + if(possible_command.empty() || !islower(possible_command[0]) || !isalpha(possible_command[0])) { + return; + } + + string cmd = shift_parameter(possible_command); + + // Act depending on command + if (cmd == "fire") { + string power = shift_parameter(possible_command); + if(power.empty()) { + msgp->stream->printf("Usage: fire power%%|off\n"); + return; + } + + float p; + if(power == "off" || power == "0") { + p= 0; + msgp->stream->printf("turning laser off and returning to auto mode\n"); - if (this->ttl_used) { - Block* block = static_cast(argument); - // Only switch TTL off if this is the last block for this move - G2/3 are multiple blocks - if (block->final_rate == 0) - this->ttl_pin->set(0); + }else{ + p= strtof(power.c_str(), NULL); + p= confine(p, 0.0F, 100.0F); + msgp->stream->printf("WARNING: Firing laser at %1.2f%% power, entering manual mode use fire off to return to auto mode\n", p); + } + + p= p/100.0F; + manual_fire= set_laser_power(p); } } -// Set laser power at the beginning of a block -void Laser::on_block_begin(void* argument){ - this->set_proportional_power(); +// returns instance +void Laser::on_get_public_data(void* argument) +{ + PublicDataRequest* pdr = static_cast(argument); + if(!pdr->starts_with(laser_checksum)) return; + pdr->set_data_ptr(this); + pdr->set_taken(); } -// Turn laser on/off depending on received GCodes -void Laser::on_gcode_execute(void* argument){ - Gcode* gcode = static_cast(argument); - this->laser_on = false; - if( gcode->has_g){ - int code = gcode->g; - if( code == 0 ){ // G0 - this->pwm_pin->write(this->pwm_inverting ? 1 - this->laser_minimum_power : this->laser_minimum_power); - this->laser_on = false; - }else if( code >= 1 && code <= 3 ){ // G1, G2, G3 - this->laser_on = true; + +void Laser::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + + // M codes execute immediately + if (gcode->has_m) { + if (gcode->m == 221) { // M221 S100 change laser power by percentage S + if(gcode->has_letter('S')) { + this->scale= gcode->get_value('S') / 100.0F; + + } else { + gcode->stream->printf("Laser power scale at %6.2f %%\n", this->scale * 100.0F); + } } } +} - if ( gcode->has_letter('S' )){ - float requested_power = gcode->get_value('S') / this->laser_maximum_s_value; - // Ensure we can't exceed maximum power - if (requested_power > 1) - requested_power = 1; - - this->laser_power = requested_power; +// calculates the current speed ratio from the currently executing block +float Laser::current_speed_ratio(const Block *block) const +{ + // find the primary moving actuator (the one with the most steps) + size_t pm= 0; + uint32_t max_steps= 0; + for (size_t i = 0; i < THEROBOT->get_number_registered_motors(); i++) { + // find the motor with the most steps + if(block->steps[i] > max_steps) { + max_steps= block->steps[i]; + pm= i; + } } - if (this->ttl_used) - this->ttl_pin->set(this->laser_on); + // figure out the ratio of its speed, from 0 to 1 based on where it is on the trapezoid, + // this is based on the fraction it is of the requested rate (nominal rate) + float ratio= block->get_trapezoid_rate(pm) / block->nominal_rate; + return ratio; } -// We follow the stepper module here, so speed must be proportional -void Laser::on_speed_change(void* argument){ - if( this->laser_on ){ - this->set_proportional_power(); +// get laser power for the currently executing block, returns false if nothing running or a G0 +bool Laser::get_laser_power(float& power) const +{ + const Block *block = StepTicker::getInstance()->get_current_block(); + + // Note to avoid a race condition where the block is being cleared we check the is_ready flag which gets cleared first, + // as this is an interrupt if that flag is not clear then it cannot be cleared while this is running and the block will still be valid (albeit it may have finished) + if(block != nullptr && block->is_ready && block->is_g123) { + float requested_power = ((float)block->s_value/(1<<11)) / this->laser_maximum_s_value; // s_value is 1.11 Fixed point + float ratio = current_speed_ratio(block); + power = requested_power * ratio * scale; + + return true; } + + return false; } -void Laser::set_proportional_power(){ - if( this->laser_on && THEKERNEL->stepper->get_current_block() ){ +// called every millisecond from timer ISR +uint32_t Laser::set_proportional_power(uint32_t dummy) +{ + if(manual_fire) return 0; + + float power; + if(get_laser_power(power)) { // adjust power to maximum power and actual velocity - float proportional_power = (((this->laser_maximum_power-this->laser_minimum_power)*(this->laser_power * THEKERNEL->stepper->get_trapezoid_adjusted_rate() / THEKERNEL->stepper->get_current_block()->nominal_rate))+this->laser_minimum_power); - this->pwm_pin->write(this->pwm_inverting ? 1 - proportional_power : proportional_power); + float proportional_power = ( (this->laser_maximum_power - this->laser_minimum_power) * power ) + this->laser_minimum_power; + set_laser_power(proportional_power); + + } else if(laser_on) { + // turn laser off + set_laser_power(0); + } + return 0; +} + +bool Laser::set_laser_power(float power) +{ + // Ensure power is >=0 and <= 1 + power= confine(power, 0.0F, 1.0F); + + if(power > 0.00001F) { + this->pwm_pin->write(this->pwm_inverting ? 1 - power : power); + if(!laser_on && this->ttl_used) this->ttl_pin->set(true); + laser_on = true; + + }else{ + this->pwm_pin->write(this->pwm_inverting ? 1 : 0); + if (this->ttl_used) this->ttl_pin->set(false); + laser_on = false; } + + return laser_on; } void Laser::on_halt(void *argument) { if(argument == nullptr) { - // Safety check - turn laser off on halt - this->laser_on = false; - if (this->ttl_used) - this->ttl_pin->set(this->laser_on); + set_laser_power(0); + manual_fire= false; } } + +float Laser::get_current_power() const +{ + return pwm_pin->read() * 100; +} diff --git a/src/modules/tools/laser/Laser.h b/src/modules/tools/laser/Laser.h index 45d95552..7978e291 100644 --- a/src/modules/tools/laser/Laser.h +++ b/src/modules/tools/laser/Laser.h @@ -5,41 +5,49 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef LASER_MODULE_H -#define LASER_MODULE_H +#pragma once #include "libs/Module.h" +#include + namespace mbed { class PwmOut; } class Pin; +class Block; class Laser : public Module{ public: Laser(); virtual ~Laser() {}; void on_module_loaded(); - void on_block_end(void* argument); - void on_block_begin(void* argument); - void on_gcode_execute(void* argument); - void on_speed_change(void* argument); void on_halt(void* argument); + void on_gcode_received(void *argument); + void on_console_line_received(void *argument); + void on_get_public_data(void* argument); + + void set_scale(float s) { scale= s/100; } + float get_scale() const { return scale*100; } + bool set_laser_power(float p); + float get_current_power() const; private: - void set_proportional_power(); + uint32_t set_proportional_power(uint32_t dummy); + bool get_laser_power(float& power) const; + float current_speed_ratio(const Block *block) const; + mbed::PwmOut *pwm_pin; // PWM output to regulate the laser power Pin *ttl_pin; // TTL output to fire laser + float laser_maximum_power; // maximum allowed laser power to be output on the pwm pin + float laser_minimum_power; // value used to tickle the laser on moves. Also minimum value for auto-scaling + float laser_maximum_s_value; // Value of S code that will represent max power + float scale; struct { - bool laser_on:1; // Laser status + bool laser_on:1; // set if the laser is on bool pwm_inverting:1; // stores whether the PWM period should be inverted bool ttl_used:1; // stores whether we have a TTL output bool ttl_inverting:1; // stores whether the TTL output should be inverted + bool manual_fire:1; // set when manually firing }; - float laser_maximum_power; // maximum allowed laser power to be output on the pwm pin - float laser_minimum_power; // value used to tickle the laser on moves. Also minimum value for auto-scaling - float laser_power; // current laser power - float laser_maximum_s_value; // Value of S code that will represent max power }; - -#endif diff --git a/src/modules/tools/spindle/Spindle.cpp b/src/modules/tools/spindle/Spindle.cpp index 9267f43c..b8e12945 100644 --- a/src/modules/tools/spindle/Spindle.cpp +++ b/src/modules/tools/spindle/Spindle.cpp @@ -97,6 +97,7 @@ void Spindle::on_module_loaded() PinName pinname = port_pin((PortName)smoothie_pin->port_number, smoothie_pin->pin_number); feedback_pin = new InterruptIn(pinname); //feedback_pin.rise(&on_pin_rise); // TODO need to assign interrupt routine + //NVIC_SetPriority(EINT3_IRQn, 16); //TODO Need to set to low priority } else { THEKERNEL->streams->printf("Error: Spindle feedback pin has to be on P0 or P2.\n"); delete this; @@ -187,7 +188,7 @@ void Spindle::on_gcode_received(void* argument) THEKERNEL->streams->printf("P: %0.6f I: %0.6f D: %0.6f\n", control_P_term, control_I_term, control_D_term); } else if (gcode->m == 3) { - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); // M3: Spindle on spindle_on = true; @@ -197,13 +198,13 @@ void Spindle::on_gcode_received(void* argument) } } else if (gcode->m == 5) { - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); spindle_on = false; } }else if(!gcode->has_m && !gcode->has_g && gcode->has_letter('S')) { // special case S on its own - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); target_rpm = gcode->get_value('S'); } } diff --git a/src/modules/tools/switch/Switch.cpp b/src/modules/tools/switch/Switch.cpp index babf4983..5e1831b4 100644 --- a/src/modules/tools/switch/Switch.cpp +++ b/src/modules/tools/switch/Switch.cpp @@ -24,7 +24,10 @@ #include "PwmOut.h" -#define switch_checksum CHECKSUM("switch") +//TODO ADDBACK #include "MRI_Hooks.h" + +#include + #define startup_state_checksum CHECKSUM("startup_state") #define startup_value_checksum CHECKSUM("startup_value") #define input_pin_checksum CHECKSUM("input_pin") @@ -103,9 +106,9 @@ void Switch::on_config_reload(void *argument) this->sigmadelta_pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); if(this->sigmadelta_pin->connected()) { if(failsafe == 1) { - // TOADDBACKĀ set_high_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); + //TODO ADDBACK set_high_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); }else{ - // TOADDBACK set_low_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); + //TODO ADDBACK set_low_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); } }else{ this->output_type= NONE; @@ -119,9 +122,9 @@ void Switch::on_config_reload(void *argument) this->digital_pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); if(this->digital_pin->connected()) { if(failsafe == 1) { - //TOADDBACK set_high_on_debug(digital_pin->port_number, digital_pin->pin); + //TODO ADDBACK set_high_on_debug(digital_pin->port_number, digital_pin->pin); }else{ - //TOADDBACK set_low_on_debug(digital_pin->port_number, digital_pin->pin); + //TODO ADDBACK set_low_on_debug(digital_pin->port_number, digital_pin->pin); } }else{ this->output_type= NONE; @@ -135,9 +138,9 @@ void Switch::on_config_reload(void *argument) pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); this->pwm_pin= pin->hardware_pwm(); if(failsafe == 1) { - //TOADDBACK set_high_on_debug(pin->port_number, pin->pin); + //TODO ADDBACK set_high_on_debug(pin->port_number, pin->pin); }else{ - //TOADDBACK set_low_on_debug(pin->port_number, pin->pin); + //TODO ADDBACK set_low_on_debug(pin->port_number, pin->pin); } delete pin; if(this->pwm_pin == nullptr) { @@ -246,20 +249,20 @@ void Switch::on_gcode_received(void *argument) int v = roundf(gcode->get_value('S') * sigmadelta_pin->max_pwm() / 255.0F); // scale by max_pwm so input of 255 and max_pwm of 128 would set value to 128 if(v != this->sigmadelta_pin->get_pwm()){ // optimize... ignore if already set to the same pwm // drain queue - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); this->sigmadelta_pin->pwm(v); this->switch_state= (v > 0); } } else { // drain queue - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); this->sigmadelta_pin->pwm(this->switch_value); this->switch_state= (this->switch_value > 0); } } else if (this->output_type == HWPWM) { // drain queue - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); // PWM output pin set duty cycle 0 - 100 if(gcode->has_letter('S')) { float v = gcode->get_value('S'); @@ -274,7 +277,7 @@ void Switch::on_gcode_received(void *argument) } else if (this->output_type == DIGITAL) { // drain queue - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); // logic pin turn on this->digital_pin->set(true); this->switch_state = true; @@ -282,7 +285,7 @@ void Switch::on_gcode_received(void *argument) } else if(match_input_off_gcode(gcode)) { // drain queue - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); this->switch_state = false; if (this->output_type == SIGMADELTA) { // SIGMADELTA output pin diff --git a/src/modules/tools/temperaturecontrol/TemperatureControl.cpp b/src/modules/tools/temperaturecontrol/TemperatureControl.cpp index e27eed89..3f679950 100644 --- a/src/modules/tools/temperaturecontrol/TemperatureControl.cpp +++ b/src/modules/tools/temperaturecontrol/TemperatureControl.cpp @@ -31,6 +31,7 @@ // Temp sensor implementations: #include "Thermistor.h" #include "max31855.h" +#include "AD8495.h" // TOADDBACK #include "MRI_Hooks.h" @@ -63,6 +64,9 @@ #define preset1_checksum CHECKSUM("preset1") #define preset2_checksum CHECKSUM("preset2") +#define runaway_range_checksum CHECKSUM("runaway_range") +#define runaway_heating_timeout_checksum CHECKSUM("runaway_heating_timeout") + TemperatureControl::TemperatureControl(uint16_t name, int index) { name_checksum= name; @@ -114,7 +118,7 @@ void TemperatureControl::on_main_loop(void *argument) { if (this->temp_violated) { this->temp_violated = false; - THEKERNEL->streams->printf("Error: MINTEMP or MAXTEMP triggered on %s. Check your temperature sensors!\n", designator.c_str()); + THEKERNEL->streams->printf("ERROR: MINTEMP or MAXTEMP triggered on %s. Check your temperature sensors!\n", designator.c_str()); THEKERNEL->streams->printf("HALT asserted - reset or M999 required\n"); THEKERNEL->call_event(ON_HALT, nullptr); } @@ -128,10 +132,14 @@ void TemperatureControl::load_config() this->set_m_code = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, set_m_code_checksum)->by_default(104)->as_number(); this->set_and_wait_m_code = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, set_and_wait_m_code_checksum)->by_default(109)->as_number(); this->get_m_code = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, get_m_code_checksum)->by_default(105)->as_number(); - this->readings_per_second = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, readings_per_second_checksum)->by_default(160)->as_number(); + this->readings_per_second = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, readings_per_second_checksum)->by_default(20)->as_number(); this->designator = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, designator_checksum)->by_default(string("T"))->as_string(); + // Runaway parameters + this->runaway_range = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, runaway_range_checksum)->by_default(0)->as_number(); + this->runaway_heating_timeout = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, runaway_heating_timeout_checksum)->by_default(0)->as_number(); + // Max and min temperatures we are not allowed to get over (Safety) this->max_temp = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, max_temp_checksum)->by_default(300)->as_number(); this->min_temp = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, min_temp_checksum)->by_default(0)->as_number(); @@ -156,8 +164,8 @@ void TemperatureControl::load_config() sensor = new Thermistor(); } else if(sensor_type.compare("max31855") == 0) { sensor = new Max31855(); - //TOADDBACK } else if(sensor_type.compare("ad8495") == 0) { - //TOADDBACK sensor = new AD8495(); + } else if(sensor_type.compare("ad8495") == 0) { + sensor = new AD8495(); } else { sensor = new TempSensor(); // A dummy implementation } @@ -310,7 +318,7 @@ void TemperatureControl::on_gcode_received(void *argument) if(this->active) { // required so temp change happens in order - THEKERNEL->conveyor->wait_for_empty_queue(); + THEKERNEL->conveyor->wait_for_idle(); float v = gcode->get_value('S'); @@ -509,8 +517,46 @@ void TemperatureControl::pid_process(float temperature) void TemperatureControl::on_second_tick(void *argument) { + + // If waiting for a temperature to be reach, display it to keep host programs up to date on the progress if (waiting) THEKERNEL->streams->printf("%s:%3.1f /%3.1f @%d\n", designator.c_str(), get_temperature(), ((target_temperature <= 0) ? 0.0 : target_temperature), o); + + // Check whether or not there is a temperature runaway issue, if so stop everything and report it + if(THEKERNEL->is_halted()) return; + + if( this->target_temperature <= 0 ){ // If we are not trying to heat, state is NOT_HEATING + this->runaway_state = NOT_HEATING; + }else{ + switch( this->runaway_state ){ + case NOT_HEATING: // If we were previously not trying to heat, but we are now, change to state WAITING_FOR_TEMP_TO_BE_REACHED + if( this->target_temperature > 0 ){ + this->runaway_state = WAITING_FOR_TEMP_TO_BE_REACHED; + this->runaway_heating_timer = 0; + } + break; + case WAITING_FOR_TEMP_TO_BE_REACHED: // In we are in state 1 ( waiting for temperature to be reached ), and the temperature has been reached, change to state TARGET_TEMPERATURE_REACHED + if( this->get_temperature() >= this->target_temperature ){ + this->runaway_state = TARGET_TEMPERATURE_REACHED; + } + this->runaway_heating_timer++; + if( this->runaway_heating_timer > this->runaway_heating_timeout && this->runaway_heating_timeout != 0 ){ + this->runaway_heating_timer = 0; + THEKERNEL->streams->printf("ERROR: Temperature took too long to be reached on %s, HALT asserted, TURN POWER OFF IMMEDIATELY - reset or M999 required\n", designator.c_str()); + THEKERNEL->call_event(ON_HALT, nullptr); + } + break; + case TARGET_TEMPERATURE_REACHED: { // If we are in state TARGET_TEMPERATURE_REACHED, check for thermal runaway + float delta= this->get_temperature() - this->target_temperature; + // If the temperature is outside the acceptable range + if(this->runaway_range != 0 && fabsf(delta) > this->runaway_range){ + THEKERNEL->streams->printf("ERROR: Temperature runaway on %s (delta temp %f), HALT asserted, TURN POWER OFF IMMEDIATELY - reset or M999 required\n", designator.c_str(), delta); + THEKERNEL->call_event(ON_HALT, nullptr); + } + } + break; + } + } } void TemperatureControl::setPIDp(float p) diff --git a/src/modules/tools/temperaturecontrol/TemperatureControl.h b/src/modules/tools/temperaturecontrol/TemperatureControl.h index aa5ef4a2..2c956449 100644 --- a/src/modules/tools/temperaturecontrol/TemperatureControl.h +++ b/src/modules/tools/temperaturecontrol/TemperatureControl.h @@ -30,6 +30,8 @@ class TemperatureControl : public Module { void set_desired_temperature(float desired_temperature); float get_temperature(); + + enum RUNAWAY_TYPE {NOT_HEATING, WAITING_FOR_TEMP_TO_BE_REACHED, TARGET_TEMPERATURE_REACHED}; friend class PID_Autotuner; @@ -80,7 +82,15 @@ class TemperatureControl : public Module { float d_factor; float PIDdt; + // Temperature runaway values + RUNAWAY_TYPE runaway_state; + + struct { + uint8_t runaway_heating_timer:8; + // Temperature runaway config options + uint8_t runaway_range:8; + uint8_t runaway_heating_timeout:8; bool use_bangbang:1; bool waiting:1; bool temp_violated:1; diff --git a/src/modules/tools/temperaturecontrol/TemperatureControlPool.h b/src/modules/tools/temperaturecontrol/TemperatureControlPool.h index 9544ee13..c0d87678 100644 --- a/src/modules/tools/temperaturecontrol/TemperatureControlPool.h +++ b/src/modules/tools/temperaturecontrol/TemperatureControlPool.h @@ -13,10 +13,6 @@ class TemperatureControlPool { public: void load_tools(); - const std::vector& get_controllers() const { return controllers; }; - - private: - std::vector controllers; }; diff --git a/src/modules/tools/temperaturecontrol/max31855.cpp b/src/modules/tools/temperaturecontrol/max31855.cpp index 62189686..7bee48b1 100644 --- a/src/modules/tools/temperaturecontrol/max31855.cpp +++ b/src/modules/tools/temperaturecontrol/max31855.cpp @@ -11,7 +11,7 @@ #include "Config.h" #include "checksumm.h" #include "ConfigValue.h" - +#include "StreamOutputPool.h" #include "max31855.h" // TOADDBACK #include "MRI_Hooks.h" @@ -32,12 +32,11 @@ Max31855::~Max31855() // Get configuration from the config file void Max31855::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) { - // Chip select this->spi_cs_pin.from_string(THEKERNEL->config->value(module_checksum, name_checksum, chip_select_checksum)->by_default("0.16")->as_string()); this->spi_cs_pin.set(true); this->spi_cs_pin.as_output(); - + // select which SPI channel to use int spi_channel = THEKERNEL->config->value(module_checksum, name_checksum, spi_channel_checksum)->by_default(0)->as_number(); PinName miso; @@ -58,7 +57,6 @@ void Max31855::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) // Spi settings: 1MHz (default), 16 bits, mode 0 (default) spi->format(16); //TODO the robot.cpp uses spi at 8 bits for the tmc2130 stepper motor in StepperMotor.cpp so we need to perhaps change this to 8 bits - } float Max31855::get_temperature() @@ -96,7 +94,7 @@ float Max31855::read_temp() // uint16_t data2 = spi->write(0); this->spi_cs_pin.set(true); - + float temperature; //Process temp @@ -117,5 +115,5 @@ float Max31855::read_temp() temperature = ((data & 0x1FFF) + 1) / -4.f; } } - return temperature; + return temperature; } diff --git a/src/modules/tools/temperaturecontrol/max31855.h b/src/modules/tools/temperaturecontrol/max31855.h index 9f85f3c1..b0ee9985 100644 --- a/src/modules/tools/temperaturecontrol/max31855.h +++ b/src/modules/tools/temperaturecontrol/max31855.h @@ -23,7 +23,7 @@ class Max31855 : public TempSensor float get_temperature(); private: - float read_temp(); + float read_temp(); Pin spi_cs_pin; mbed::SPI *spi; RingBuffer readings; diff --git a/src/modules/tools/temperaturecontrol/predefined_thermistors.h b/src/modules/tools/temperaturecontrol/predefined_thermistors.h index c8fc71c5..f2400d9f 100644 --- a/src/modules/tools/temperaturecontrol/predefined_thermistors.h +++ b/src/modules/tools/temperaturecontrol/predefined_thermistors.h @@ -5,6 +5,8 @@ typedef struct { float beta, r0, t0; } const thermistor_beta_table_t; +// NOTE the order of these tables must NOT be changed as the index can be used in M305 to set the prefined thermistor to use + static const thermistor_beta_table_t predefined_thermistors_beta[] { // name, r1, r2, beta, r0, t0 {"EPCOS100K", 0, 4700, 4066.0F, 100000.0F, 25.0F}, // B57540G0104F000 diff --git a/src/modules/tools/toolmanager/Tool.h b/src/modules/tools/toolmanager/Tool.h index 7f5ae4c8..d7a50e24 100644 --- a/src/modules/tools/toolmanager/Tool.h +++ b/src/modules/tools/toolmanager/Tool.h @@ -5,8 +5,7 @@ you should have received a copy of the gnu general public license along with smoothie. if not, see . */ -#ifndef TOOL_H -#define TOOL_H +#pragma once #include "Module.h" @@ -18,16 +17,12 @@ class Tool : public Module Tool(){}; virtual ~Tool() {}; - virtual void enable(){ enabled= true; } - virtual void disable(){ enabled= false; } + virtual void select()= 0; + virtual void deselect()= 0; virtual const float *get_offset() const { return offset; } virtual uint16_t get_name() const { return identifier; } protected: - bool enabled; float offset[3]; uint16_t identifier; }; - - -#endif diff --git a/src/modules/tools/toolmanager/ToolManager.cpp b/src/modules/tools/toolmanager/ToolManager.cpp index be0b197e..16680101 100644 --- a/src/modules/tools/toolmanager/ToolManager.cpp +++ b/src/modules/tools/toolmanager/ToolManager.cpp @@ -55,15 +55,15 @@ void ToolManager::on_gcode_received(void *argument) } else { if(new_tool != this->active_tool) { // We must wait for an empty queue before we can disable the current extruder - THEKERNEL->conveyor->wait_for_empty_queue(); - this->tools[active_tool]->disable(); + THEKERNEL->conveyor->wait_for_idle(); + this->tools[active_tool]->deselect(); this->active_tool = new_tool; this->current_tool_name = this->tools[active_tool]->get_name(); - this->tools[active_tool]->enable(); + this->tools[active_tool]->select(); //send new_tool_offsets to robot const float *new_tool_offset = tools[new_tool]->get_offset(); - THEKERNEL->robot->setToolOffset(new_tool_offset); + THEROBOT->setToolOffset(new_tool_offset); } } } @@ -115,13 +115,13 @@ void ToolManager::on_set_public_data(void* argument) void ToolManager::add_tool(Tool* tool_to_add) { if(this->tools.size() == 0) { - tool_to_add->enable(); + tool_to_add->select(); this->current_tool_name = tool_to_add->get_name(); //send new_tool_offsets to robot const float *new_tool_offset = tool_to_add->get_offset(); - THEKERNEL->robot->setToolOffset(new_tool_offset); + THEROBOT->setToolOffset(new_tool_offset); } else { - tool_to_add->disable(); + tool_to_add->deselect(); } this->tools.push_back( tool_to_add ); } diff --git a/src/modules/utils/PlayLed/PlayLed.cpp b/src/modules/utils/PlayLed/PlayLed.cpp index af06db91..8e593303 100644 --- a/src/modules/utils/PlayLed/PlayLed.cpp +++ b/src/modules/utils/PlayLed/PlayLed.cpp @@ -53,7 +53,7 @@ uint32_t PlayLed::led_tick(uint32_t) if(++cnt >= 6) { // 6 ticks ~ 500ms cnt= 0; - led.set(!THEKERNEL->conveyor->is_queue_empty()); + led.set(!THECONVEYOR->is_idle()); } return 0; diff --git a/src/modules/utils/currentcontrol/ad5206.h b/src/modules/utils/currentcontrol/ad5206.h index 6d8dc112..b6a986a5 100644 --- a/src/modules/utils/currentcontrol/ad5206.h +++ b/src/modules/utils/currentcontrol/ad5206.h @@ -11,8 +11,9 @@ class AD5206 : public DigipotBase { public: AD5206(){ - //SPI0_SCK - //this->spi= new mbed::SPI(P0_9,P0_8,P0_7); //should be able to set those pins in config + //TODO convert to use SPI channels + //SPI0_SCK + //this->spi= new mbed::SPI(P0_9,P0_8,P0_7); //should be able to set those pins in config this->spi= new mbed::SPI(SPI0_MOSI,SPI0_MISO,SPI0_SCK); //should be able to set those pins in config cs.from_string("4.29")->as_output(); //this also should be configurable cs.set(1); diff --git a/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp b/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp index ee64570d..7110a5f6 100644 --- a/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp +++ b/src/modules/utils/motordrivercontrol/drivers/TMC21X/TMC21X.cpp @@ -1113,7 +1113,7 @@ void TMC21X::dumpStatus(StreamOutput *stream, bool readable) //TODO Convert bool moving = THEKERNEL->robot->actuators[0]->is_moving(); // dump out in the format that the processing script needs if (moving) { - stream->printf("#sg%d,p%lu,k%u,r,", getCurrentStallGuardReading(), THEKERNEL->robot->actuators[0]->get_stepped(), getCoolstepCurrent()); + stream->printf("#sg%d,p%lu,k%u,r,", getCurrentStallGuardReading(), THEKERNEL->robot->actuators[0]->get_current_step(), getCoolstepCurrent()); } else { readStatus(TMC21X_READOUT_POSITION); // get the status bits stream->printf("#s,"); diff --git a/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp index d9a52224..0c432938 100644 --- a/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp +++ b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp @@ -896,18 +896,18 @@ void TMC26X::dumpStatus(StreamOutput *stream, bool readable) } else { // TODO hardcoded for X need to select ABC as needed - bool moving = THEKERNEL->robot->actuators[0]->is_moving(); + bool moving = THEROBOT->actuators[0]->is_moving(); // dump out in the format that the processing script needs if (moving) { - stream->printf("#sg%d,p%lu,k%u,r,", getCurrentStallGuardReading(), THEKERNEL->robot->actuators[0]->get_stepped(), getCoolstepCurrent()); + stream->printf("#sg%d,p%lu,k%u,r,", getCurrentStallGuardReading(), THEROBOT->actuators[0]->get_current_step(), getCoolstepCurrent()); } else { readStatus(TMC26X_READOUT_POSITION); // get the status bits stream->printf("#s,"); } - stream->printf("d%d,", THEKERNEL->robot->actuators[0]->which_direction() ? 1 : -1); + stream->printf("d%d,", THEROBOT->actuators[0]->which_direction() ? 1 : -1); stream->printf("c%u,m%d,", getCurrent(), getMicrosteps()); // stream->printf('S'); - // stream->printf(TMC26XStepper.getSpeed(), DEC); + // stream->printf(tmc26XStepper.getSpeed(), DEC); stream->printf("t%d,f%d,", getStallGuardThreshold(), getStallGuardFilter()); //print out the general cool step config From 429dc877cc444995a85eefdcb2e6bc0899e5683a Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 09:43:55 +1300 Subject: [PATCH 14/66] modified: modules/tools/temperaturecontrol/TemperatureControlPool.cpp --- src/modules/tools/temperaturecontrol/TemperatureControlPool.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/tools/temperaturecontrol/TemperatureControlPool.cpp b/src/modules/tools/temperaturecontrol/TemperatureControlPool.cpp index 65b8ed64..debd8bbf 100644 --- a/src/modules/tools/temperaturecontrol/TemperatureControlPool.cpp +++ b/src/modules/tools/temperaturecontrol/TemperatureControlPool.cpp @@ -22,7 +22,6 @@ using namespace std; void TemperatureControlPool::load_tools() { - vector modules; THEKERNEL->config->get_module_list( &modules, temperature_control_checksum ); int cnt = 0; @@ -30,7 +29,6 @@ void TemperatureControlPool::load_tools() // If module is enabled if( THEKERNEL->config->value(temperature_control_checksum, cs, enable_checksum )->as_bool() ) { TemperatureControl *controller = new TemperatureControl(cs, cnt++); - controllers.push_back( cs ); THEKERNEL->add_module(controller); } } From e24ba76eae5a34a2d6c94d82defad27852fc72a3 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 09:52:56 +1300 Subject: [PATCH 15/66] modified: ../README.md --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8ce377e7..2cbabe0b 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,14 @@ A Smoothie firmware port to the Bambino210E board ahead of making changes to sup There are no guarantees that this code works, won't burn down your house or destroy your electronics or hardware, use entirely at your own risk. -There are a lot of bugs in the code and it is very much a work in progress, the reason it is published is to allow others to contriute before it is folded back into the Smoothie2 main repo. +There are a lot of bugs in the code and it is very much a work in progress, the reason it is published is to allow others to contriute before it is folded back into the Smoothie2 main repo. -Current status : +#IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED + +Current status as at 2016-10-27: * This code base is equivalent to Smoothie V1 as at 2016-10-18. -* The Extruder code is being worked on and as of 2016-10-22 and does not yet compile. +* The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) +* More code porting underway, expected completion is 2016-10-31 * Some major lower level things need implementation ( USB, Ethernet, SDIO ). # Compiling @@ -39,26 +42,23 @@ And finally compile the code # TODO :Ā  Current major TODOs :Ā  - * Complete the Extruder port (IT DOES NOT YET COMPILE, STILL AN ALPHA PORT) + * Complete the three way comparison between 'old Smoothie - pre-major rewrite in June/July 2016' 'Smoothie V1 (current)' and 'SmoothieV2' * SD-Card, USB and Ethernet need to be implemented. * Move the step generation to the M0 co-processor instead of on the M4 main core. TODOĀ functionality to port from v1 in more detail : * libs/Adc.cpp : Port low level ADC functionality, then re-enable it in TemperatureControl ( note :Ā there is some weird pin configuration surrounding this that makes it more complicated than it seems, but I don't remember what it is. Logxen@gmail.com probably remembers what it is ). * libs/md5.cpp : Port and re-enable in SimpleShell - * libs/Network : Port and enable + * libs/Network : Port and enable once base libraries are available * [@adamgreen](https://github.com/adamgreen) - libs/SDFAT.cpp : Port for SPI SD card access, not SDIO, and re-enable. Then add SDIOĀ support. * [@adamgreen](https://github.com/adamgreen) - libs/SPI.cpp : Port and make to use the mBed library instead of registers - * libs/USBDevice : Port and enable + * libs/USBDevice : Port and enable once base libraries are available * libs/Watchdog : Port ( no mBed libraries available that I could find ) * libs/Hook.h : Uses doubles instead of ints ( in v1 ) for some reason, should be fixed - * modules/tools/drillingcycles : Port ( should be fairly simple ) - * modules/tools/extruder :Ā Port Under way - * modules/tools/filamentdetector : Port * modules/tools/filamentdetector : Get PWM to work * modules/tools/spindle :Ā Get PWM to work - * modules/tools/temperaturecontrol : Finish porting, get the ADC to actually work, port the AD8495 temperature input - * modules/tools/zprobe :Ā Port ( could be quite easy ) + * modules/tools/temperaturecontrol : Finish porting, get the ADC to actually work, I cannot test the AD8495 temperature inputs as I don't have one of these! + * modules/tools/zprobe :Ā Port * modules/tools/simpleshell :Ā Port * The FPGA requires a SGPIO spi library @@ -67,9 +67,8 @@ TODOĀ functionality to port from v1 in more detail : Things that are broken during the port and should be added back as things progress ( some things might be missing here. I try to use the TOADDBACK label when commenting things so search for that too ) :Ā  * In GcodeDispatch.cpp : Removed dependency to Pauser.h -* In Switch.cpp : Commented out all of the set_low_on_debug stuff +* In Switch.cpp : Commented out all of the set_low_on_debug stuff; do we still need this? * In SlowTicker.h and Hook.h : Using doubles instead of ints for intervals/frequencies and counting down. Done to accomodate mBed, but dirty costly hack -* In SlowTicker.h : Assuming it's fine to use mBed for this, we can remove a lot of code and just rely fully on mBed * In RingBuffer.h : Removed the irq stuff * In Laser.cpp : PWM pin is broken, needs to be fixed From 29b8eb561a47540e3a40a79373b2d3c7dbc41ce4 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 11:31:31 +1300 Subject: [PATCH 16/66] modified: ../README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cbabe0b..744fe184 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Smoothie firmware port to the Bambino210E board ahead of making changes to sup There are no guarantees that this code works, won't burn down your house or destroy your electronics or hardware, use entirely at your own risk. -There are a lot of bugs in the code and it is very much a work in progress, the reason it is published is to allow others to contriute before it is folded back into the Smoothie2 main repo. +There are a lot of bugs in the code and it is very much a work in progress, the reason it is published is to allow others to contribute before it is folded back into the Smoothie2 main repo. #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED From 994239d187a7e13bef3bff1a606607a76d5bbd5d Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 12:39:26 +1300 Subject: [PATCH 17/66] modified: libs/Kernel.cpp --- src/libs/Kernel.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 38a7ef7d..d20951be 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -80,6 +80,35 @@ Kernel::Kernel(){ this->current_path = "/"; + +/* +//TODO Need to add back as part of MRI and remove the AHB0 for the serial console + // Configure UART depending on MRI config + // Match up the SerialConsole to MRI UART. This makes it easy to use only one UART for both debug and actual commands. + NVIC_SetPriorityGrouping(0); + +#if MRI_ENABLE != 0 + switch( __mriPlatform_CommUartIndex() ) { + case 0: + this->serial = new(AHB0) SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + case 1: + this->serial = new(AHB0) SerialConsole( p13, p14, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + case 2: + this->serial = new(AHB0) SerialConsole( p28, p27, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + case 3: + this->serial = new(AHB0) SerialConsole( p9, p10, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + } +#endif + // default + if(this->serial == NULL) { + this->serial = new(AHB0) SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + } + +*/ //some boards don't have leds.. TOO BAD! this->use_leds= !this->config->value( disable_leds_checksum )->by_default(false)->as_bool(); @@ -92,6 +121,41 @@ Kernel::Kernel(){ // we exepct ok per line now not per G code, setting this to false will return to the old (incorrect) way of ok per G code this->ok_per_line= this->config->value( ok_per_line_checksum )->by_default(true)->as_bool(); + this->add_module( this->serial ); + + // HAL stuff + add_module( this->slow_ticker = new SlowTicker()); + + this->step_ticker = new StepTicker(); + this->adc = new Adc(); + + // TODO : These should go into platform-specific files + // LPC17xx-specific + //NVIC_SetPriorityGrouping(0); + NVIC_SetPriority(TIMER0_IRQn, 2); + NVIC_SetPriority(TIMER1_IRQn, 1); + //NVIC_SetPriority(TIMER2_IRQn, 4); + NVIC_SetPriority(PendSV_IRQn, 3); + + // Set other priorities lower than the timers + //NVIC_SetPriority(ADC_IRQn, 5); + //NVIC_SetPriority(USB_IRQn, 5); + +/* +//TODO Need to add back as part of MRI and remove the AHB0 for the serial console + // If MRI is enabled + if( MRI_ENABLE ){ + if( NVIC_GetPriority(UART0_IRQn) > 0 ){ NVIC_SetPriority(UART0_IRQn, 5); } + if( NVIC_GetPriority(UART1_IRQn) > 0 ){ NVIC_SetPriority(UART1_IRQn, 5); } + if( NVIC_GetPriority(UART2_IRQn) > 0 ){ NVIC_SetPriority(UART2_IRQn, 5); } + if( NVIC_GetPriority(UART3_IRQn) > 0 ){ NVIC_SetPriority(UART3_IRQn, 5); } + }else{ + NVIC_SetPriority(UART0_IRQn, 5); + NVIC_SetPriority(UART1_IRQn, 5); + NVIC_SetPriority(UART2_IRQn, 5); + NVIC_SetPriority(UART3_IRQn, 5); + } +*/ // Configure the step ticker this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); // float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); From 44092229043e9ffaa110052b29913fb4bb5dbab8 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 12:55:05 +1300 Subject: [PATCH 18/66] modified: libs/Adc.cpp modified: libs/RingBuffer.h modified: libs/utils.cpp modified: libs/utils.h modified: libs/nuts_bolts.h --- src/libs/Adc.cpp | 22 +++++++++++----------- src/libs/RingBuffer.h | 1 + src/libs/utils.cpp | 34 ++++++++++++++++++++++++++++------ src/libs/utils.h | 37 ++++++++++++++++++------------------- 4 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/libs/Adc.cpp b/src/libs/Adc.cpp index 82c0fd68..7245f08d 100644 --- a/src/libs/Adc.cpp +++ b/src/libs/Adc.cpp @@ -3,7 +3,7 @@ Smoothie 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. Smoothie 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 Smoothie. If not, see . - */ +*/ #include "Adc.h" #include "libs/nuts_bolts.h" @@ -87,8 +87,8 @@ unsigned int Adc::read(Pin *pin) __enable_irq(); #ifdef USE_MEDIAN_FILTER - // returns the median value of the last 8 samples - return median_buffer[quick_median(median_buffer, num_samples)]; + // returns the median value of the last 8 samples + return median_buffer[quick_median(median_buffer, num_samples)]; #elif defined(OVERSAMPLE) // Oversample to get 2 extra bits of resolution @@ -98,7 +98,7 @@ unsigned int Adc::read(Pin *pin) std::sort(median_buffer, median_buffer + num_samples); uint32_t sum = 0; for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) { - sum += median_buffer[i]; + sum += median_buffer[i]; } // this slows down the rate of change a little bit ave_buf[channel][3]= ave_buf[channel][2]; @@ -111,13 +111,13 @@ unsigned int Adc::read(Pin *pin) return tValue; #else - // sort the 8 readings and return the average of the middle 4 - std::sort(median_buffer, median_buffer + num_samples); - int sum = 0; - for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) { - sum += median_buffer[i]; - } - return sum / (num_samples / 2); + // sort the 8 readings and return the average of the middle 4 + std::sort(median_buffer, median_buffer + num_samples); + int sum = 0; + for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) { + sum += median_buffer[i]; + } + return sum / (num_samples / 2); #endif } diff --git a/src/libs/RingBuffer.h b/src/libs/RingBuffer.h index 88ea3c68..3ae9ec6b 100644 --- a/src/libs/RingBuffer.h +++ b/src/libs/RingBuffer.h @@ -3,6 +3,7 @@ Smoothie 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. Smoothie 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 Smoothie. If not, see . + With chucks taken from http://en.wikipedia.org/wiki/Circular_buffer, see licence there also */ diff --git a/src/libs/utils.cpp b/src/libs/utils.cpp index 0c197687..9bb9c8ac 100644 --- a/src/libs/utils.cpp +++ b/src/libs/utils.cpp @@ -152,6 +152,23 @@ bool file_exists( const string file_name ) return exists; } +/* +//TODO Add watchdog timer support +// Prepares and executes a watchdog reset for dfu or reboot +void system_reset( bool dfu ) +{ + if(dfu) { + LPC_WDT->WDCLKSEL = 0x1; // Set CLK src to PCLK + uint32_t clk = SystemCoreClock / 16; // WD has a fixed /4 prescaler, PCLK default is /4 + LPC_WDT->WDTC = 1 * (float)clk; // Reset in 1 second + LPC_WDT->WDMOD = 0x3; // Enabled and Reset + LPC_WDT->WDFEED = 0xAA; // Kick the dog! + LPC_WDT->WDFEED = 0x55; + } else { + NVIC_SystemReset(); + } +} +*/ // Convert a path indication ( absolute or relative ) into a path ( absolute ) string absolute_from_relative( string path ) { @@ -246,13 +263,18 @@ string wcs2gcode(int wcs) { return str; } -void safe_delay(uint32_t delayms) +void safe_delay_ms(uint32_t delay) { - uint32_t dus= delayms*1000; //convert ms to us - uint32_t counter = 0; - while( counter < dus ){ + safe_delay_us(delay*1000); +} + +void safe_delay_us(uint32_t dus) +{ + uint32_t start = us_ticker_read(); + while ((us_ticker_read() - start) < dus) { THEKERNEL->call_event(ON_IDLE); - counter++; - wait_us(1); +//TODO determine if below code is still necessary +// counter++; +// wait_us(1); } } diff --git a/src/libs/utils.h b/src/libs/utils.h index f40c20c4..d8aa7eb8 100644 --- a/src/libs/utils.h +++ b/src/libs/utils.h @@ -1,14 +1,10 @@ -#ifndef UTILS_H -#define UTILS_H +#pragma once #include #include #include -using std::string; -using std::vector; - -string lc(const string& str); +std::string lc(const std::string& str); bool is_alpha( int ); bool is_digit( int ); @@ -16,29 +12,32 @@ bool is_numeric( int ); bool is_alphanum( int ); bool is_whitespace( int ); -vector split(const char *str, char c = ','); -vector parse_number_list(const char *str); -vector parse_number_list(const char *str, uint8_t radix); +std::vector split(const char *str, char c = ','); +std::vector parse_number_list(const char *str); +std::vector parse_number_list(const char *str, uint8_t radix); -string remove_non_number( string str ); +std::string remove_non_number( std::string str ); -uint16_t get_checksum(const string& to_check); +uint16_t get_checksum(const std::string& to_check); uint16_t get_checksum(const char* to_check); -void get_checksums(uint16_t check_sums[], const string& key); +void get_checksums(uint16_t check_sums[], const std::string& key); + +std::string shift_parameter( std::string ¶meters ); -string shift_parameter( string ¶meters ); +std::string get_arguments( const std::string& possible_command ); -string get_arguments( const string& possible_command ); +bool file_exists( const std::string file_name ); -bool file_exists( const string file_name ); +void system_reset( bool dfu= false ); -string absolute_from_relative( string path ); +std::string absolute_from_relative( std::string path ); int append_parameters(char *buf, std::vector> params, size_t bufsize); -string wcs2gcode(int wcs); -void safe_delay(uint32_t delay); +std::string wcs2gcode(int wcs); +void safe_delay_us(uint32_t delay); +void safe_delay_ms(uint32_t delay); #define confine(value, min, max) (((value) < (min))?(min):(((value) > (max))?(max):(value))) -#endif +//#define dd(...) LPC_GPIO2->FIODIR = 0xffff; LPC_GPIO2->FIOCLR = 0xffff; LPC_GPIO2->FIOSET = __VA_ARGS__ From 61fde90a0e12d266cf746f21fc593339b318417c Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 12:55:39 +1300 Subject: [PATCH 19/66] new file: libs/md5.h --- src/libs/md5.h | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/libs/md5.h diff --git a/src/libs/md5.h b/src/libs/md5.h new file mode 100644 index 00000000..7645620b --- /dev/null +++ b/src/libs/md5.h @@ -0,0 +1,92 @@ +/* MD5 + converted to C++ class by Frank Thilo (thilo@unix-ag.org) + for bzflag (http://www.bzflag.org) + + based on: + + md5.h and md5.c + reference implementation of RFC 1321 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +#ifndef BZF_MD5_H +#define BZF_MD5_H + +#include + + +// a small class for calculating MD5 hashes of strings or byte arrays +// it is not meant to be fast or secure +// +// usage: 1) feed it blocks of uchars with update() +// 2) finalize() +// 3) get hexdigest() string +// or +// MD5(std::string).hexdigest() +// +// assumes that char is 8 bit and int is 32 bit +class MD5 +{ +public: + typedef unsigned int size_type; // must be 32bit + + MD5(); + MD5(const std::string &text); + void update(const unsigned char *buf, size_type length); + void update(const char *buf, size_type length); + MD5 &finalize(); + std::string hexdigest() const; + void bindigest(void *buf, int len) const; + +private: + void init(); + typedef unsigned char uint1; // 8bit + typedef unsigned int uint4; // 32bit + enum {blocksize = 64}; // VC6 won't eat a const static int here + + void transform(const uint1 block[blocksize]); + static void decode(uint4 output[], const uint1 input[], size_type len); + static void encode(uint1 output[], const uint4 input[], size_type len); + + bool finalized; + uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk + uint4 count[2]; // 64bit counter for number of bits (lo, hi) + uint4 state[4]; // digest so far + uint1 digest[16]; // the result + + // low level logic operations + static inline uint4 F(uint4 x, uint4 y, uint4 z); + static inline uint4 G(uint4 x, uint4 y, uint4 z); + static inline uint4 H(uint4 x, uint4 y, uint4 z); + static inline uint4 I(uint4 x, uint4 y, uint4 z); + static inline uint4 rotate_left(uint4 x, int n); + static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); +}; + +std::string md5(const std::string str); + +#endif From 1cc0791ae3ad8fbaef234df0be135117409d8b30 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 12:56:01 +1300 Subject: [PATCH 20/66] modified: libs/nuts_bolts.h --- src/libs/nuts_bolts.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/nuts_bolts.h b/src/libs/nuts_bolts.h index f17f4e99..5f799586 100644 --- a/src/libs/nuts_bolts.h +++ b/src/libs/nuts_bolts.h @@ -1,15 +1,19 @@ /* nuts_bolts.h - cartesian robot controller. 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 . */ From 163a9346c0d1373e3a356d5e61f07b3618ad29df Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 16:06:34 +1300 Subject: [PATCH 21/66] modified: libs/Kernel.cpp modified: libs/Kernel.h modified: modules/utils/motordrivercontrol/MotorDriverControl.cpp --- src/libs/Kernel.cpp | 64 ------------------- src/libs/Kernel.h | 1 + .../motordrivercontrol/MotorDriverControl.cpp | 18 +++++- 3 files changed, 18 insertions(+), 65 deletions(-) diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index d20951be..38a7ef7d 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -80,35 +80,6 @@ Kernel::Kernel(){ this->current_path = "/"; - -/* -//TODO Need to add back as part of MRI and remove the AHB0 for the serial console - // Configure UART depending on MRI config - // Match up the SerialConsole to MRI UART. This makes it easy to use only one UART for both debug and actual commands. - NVIC_SetPriorityGrouping(0); - -#if MRI_ENABLE != 0 - switch( __mriPlatform_CommUartIndex() ) { - case 0: - this->serial = new(AHB0) SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); - break; - case 1: - this->serial = new(AHB0) SerialConsole( p13, p14, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); - break; - case 2: - this->serial = new(AHB0) SerialConsole( p28, p27, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); - break; - case 3: - this->serial = new(AHB0) SerialConsole( p9, p10, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); - break; - } -#endif - // default - if(this->serial == NULL) { - this->serial = new(AHB0) SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); - } - -*/ //some boards don't have leds.. TOO BAD! this->use_leds= !this->config->value( disable_leds_checksum )->by_default(false)->as_bool(); @@ -121,41 +92,6 @@ Kernel::Kernel(){ // we exepct ok per line now not per G code, setting this to false will return to the old (incorrect) way of ok per G code this->ok_per_line= this->config->value( ok_per_line_checksum )->by_default(true)->as_bool(); - this->add_module( this->serial ); - - // HAL stuff - add_module( this->slow_ticker = new SlowTicker()); - - this->step_ticker = new StepTicker(); - this->adc = new Adc(); - - // TODO : These should go into platform-specific files - // LPC17xx-specific - //NVIC_SetPriorityGrouping(0); - NVIC_SetPriority(TIMER0_IRQn, 2); - NVIC_SetPriority(TIMER1_IRQn, 1); - //NVIC_SetPriority(TIMER2_IRQn, 4); - NVIC_SetPriority(PendSV_IRQn, 3); - - // Set other priorities lower than the timers - //NVIC_SetPriority(ADC_IRQn, 5); - //NVIC_SetPriority(USB_IRQn, 5); - -/* -//TODO Need to add back as part of MRI and remove the AHB0 for the serial console - // If MRI is enabled - if( MRI_ENABLE ){ - if( NVIC_GetPriority(UART0_IRQn) > 0 ){ NVIC_SetPriority(UART0_IRQn, 5); } - if( NVIC_GetPriority(UART1_IRQn) > 0 ){ NVIC_SetPriority(UART1_IRQn, 5); } - if( NVIC_GetPriority(UART2_IRQn) > 0 ){ NVIC_SetPriority(UART2_IRQn, 5); } - if( NVIC_GetPriority(UART3_IRQn) > 0 ){ NVIC_SetPriority(UART3_IRQn, 5); } - }else{ - NVIC_SetPriority(UART0_IRQn, 5); - NVIC_SetPriority(UART1_IRQn, 5); - NVIC_SetPriority(UART2_IRQn, 5); - NVIC_SetPriority(UART3_IRQn, 5); - } -*/ // Configure the step ticker this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); // float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); diff --git a/src/libs/Kernel.h b/src/libs/Kernel.h index 023c21d3..790bdfed 100644 --- a/src/libs/Kernel.h +++ b/src/libs/Kernel.h @@ -32,6 +32,7 @@ class Adc; class PublicData; //TOADDBACKĀ class SimpleShell; //TOADDBACK class Configurator; +using namespace std; class Kernel { public: diff --git a/src/modules/utils/motordrivercontrol/MotorDriverControl.cpp b/src/modules/utils/motordrivercontrol/MotorDriverControl.cpp index 4dcedf8a..4dde094a 100644 --- a/src/modules/utils/motordrivercontrol/MotorDriverControl.cpp +++ b/src/modules/utils/motordrivercontrol/MotorDriverControl.cpp @@ -47,6 +47,22 @@ MotorDriverControl::MotorDriverControl(uint8_t id) : id(id) enable_event= false; current_override= false; microstep_override= false; + chip = TMC2130; + if (chip==TMC2130) { + current = 31; // 31/31 ration driven + } else { + current = 1000; // 1000 mA + } + switch(chip) { + case DRV8711: max_current= DRV8711_max_current; break; + case TMC2660: max_current= TMC2660_max_current; break; + case TMC2130: max_current= TMC2130_max_current; break; //TMC2130 only allows a ratio of 0..31 which represents 1/32 .. 32/32 + } + current = max_current; + hold_current = max_current; + microsteps = 16; + designator = '0'; + spi = nullptr; } MotorDriverControl::~MotorDriverControl() @@ -189,7 +205,7 @@ bool MotorDriverControl::config_module(uint16_t cs) // enable alarm monitoring for the chip this->register_for_event(ON_SECOND_TICK); } -//TODO fix below to actuall print out the human readable MBED pin name, e.e P3_0 rather than its address +//TODO fix below to actually print out the human readable MBED pin name, e.e P3_0 rather than its address THEKERNEL->streams->printf("MotorDriverControl INFO: configured motor %c (%d): as %s, cs: %04X\n", designator, id, chip==TMC2660?"TMC2660":chip==DRV8711?"DRV8711":chip==TMC2130?"TMC2130":"UNKNOWN", spi_cs_pin.getPinName()); return true; From 01c9acb8666af0123924b49b94dd30123e13e980 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 16:52:21 +1300 Subject: [PATCH 22/66] modified: libs/Kernel.cpp --- src/libs/Kernel.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 38a7ef7d..00321421 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -48,6 +48,8 @@ Kernel* Kernel::instance; // The kernel is the central point in Smoothie : it stores modules, and handles event calls Kernel::Kernel(){ + + debug = false; halted= false; feed_hold= false; @@ -56,15 +58,19 @@ Kernel::Kernel(){ // All streams register to the Kernel so we can broadcast messages this->streams = new StreamOutputPool(); + this->serial = nullptr; + this->secondary_serial = nullptr; + // Create the default UART Serial Console interface - if (SMOOTHIE_UART_PRIMARY_ENABLE) { +#ifdef SMOOTHIE_UART_PRIMARY_ENABLE this->serial = new SerialConsole(SMOOTHIE_UART_PRIMARY_TX, SMOOTHIE_UART_PRIMARY_RX, 9600); this->add_module( this->serial ); - } - if (SMOOTHIE_UART_SECONDARY_ENABLE) { +#endif + +#ifdef SMOOTHIE_UART_SECONDARY_ENABLE this->secondary_serial = new SerialConsole(SMOOTHIE_UART_SECONDARY_TX, SMOOTHIE_UART_SECONDARY_RX, 9600); this->add_module( this->secondary_serial ); - } +#endif // Config next, but does not load cache yet this->config = new Config(); From 0a0fcf9df296325d3995b994eaabef53dc1da104 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 27 Oct 2016 23:55:40 +1300 Subject: [PATCH 23/66] modified: gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h modified: makefile modified: src/libs/Hook.h modified: src/modules/communication/GcodeDispatch.cpp modified: src/modules/communication/SerialConsole.cpp modified: src/modules/communication/SerialConsole.h modified: src/modules/robot/ActuatorCoordinates.h modified: src/modules/robot/Robot.h modified: src/modules/robot/arm_solutions/BaseSolution.h modified: src/modules/robot/arm_solutions/CartesianSolution.cpp modified: src/modules/robot/arm_solutions/CartesianSolution.h modified: src/modules/robot/arm_solutions/CoreXZSolution.cpp modified: src/modules/robot/arm_solutions/CoreXZSolution.h modified: src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp modified: src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h modified: src/modules/robot/arm_solutions/HBotSolution.cpp modified: src/modules/robot/arm_solutions/HBotSolution.h modified: src/modules/robot/arm_solutions/LinearDeltaSolution.cpp modified: src/modules/robot/arm_solutions/LinearDeltaSolution.h modified: src/modules/robot/arm_solutions/MorganSCARASolution.cpp modified: src/modules/robot/arm_solutions/MorganSCARASolution.h modified: src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp modified: src/modules/robot/arm_solutions/RotaryDeltaSolution.h modified: src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp modified: src/modules/robot/arm_solutions/RotatableCartesianSolution.h modified: src/modules/tools/endstops/Endstops.cpp modified: src/modules/tools/extruder/Extruder.cpp modified: src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp modified: src/modules/tools/scaracal/SCARAcal.cpp modified: src/modules/tools/spindle/Spindle.cpp modified: src/modules/tools/switch/Switch.cpp modified: src/modules/tools/temperaturecontrol/max31855.cpp modified: src/modules/tools/temperaturecontrol/predefined_thermistors.h modified: src/modules/tools/toolmanager/Tool.h new file: src/modules/tools/zprobe/DeltaCalibrationStrategy.cpp new file: src/modules/tools/zprobe/DeltaCalibrationStrategy.h new file: src/modules/tools/zprobe/DeltaGridStrategy.cpp new file: src/modules/tools/zprobe/DeltaGridStrategy.h new file: src/modules/tools/zprobe/LevelingStrategy.h new file: src/modules/tools/zprobe/Plane3D.cpp new file: src/modules/tools/zprobe/Plane3D.h new file: src/modules/tools/zprobe/ThreePointStrategy.cpp new file: src/modules/tools/zprobe/ThreePointStrategy.h new file: src/modules/tools/zprobe/ZGridStrategy.cpp new file: src/modules/tools/zprobe/ZGridStrategy.h new file: src/modules/tools/zprobe/ZProbe.cpp new file: src/modules/tools/zprobe/ZProbe.h modified: src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp --- .../mbed/libraries/mbed/hal/sleep_api.h | 3 +- makefile | 4 +- src/libs/Hook.h | 6 +- src/modules/communication/GcodeDispatch.cpp | 4 + src/modules/communication/SerialConsole.cpp | 4 +- src/modules/communication/SerialConsole.h | 2 +- src/modules/robot/ActuatorCoordinates.h | 6 +- src/modules/robot/Robot.h | 5 +- .../robot/arm_solutions/BaseSolution.h | 7 +- .../robot/arm_solutions/CartesianSolution.cpp | 4 +- .../robot/arm_solutions/CartesianSolution.h | 15 +- .../robot/arm_solutions/CoreXZSolution.cpp | 4 +- .../robot/arm_solutions/CoreXZSolution.h | 10 +- .../ExperimentalDeltaSolution.cpp | 16 +- .../arm_solutions/ExperimentalDeltaSolution.h | 18 +- .../robot/arm_solutions/HBotSolution.cpp | 6 +- .../robot/arm_solutions/HBotSolution.h | 16 +- .../arm_solutions/LinearDeltaSolution.cpp | 25 +- .../robot/arm_solutions/LinearDeltaSolution.h | 11 +- .../arm_solutions/MorganSCARASolution.cpp | 89 +-- .../robot/arm_solutions/MorganSCARASolution.h | 8 +- .../arm_solutions/RotaryDeltaSolution.cpp | 11 +- .../robot/arm_solutions/RotaryDeltaSolution.h | 15 +- .../RotatableCartesianSolution.cpp | 12 +- .../RotatableCartesianSolution.h | 14 +- src/modules/tools/endstops/Endstops.cpp | 11 +- src/modules/tools/extruder/Extruder.cpp | 45 +- .../RotaryDeltaCalibration.cpp | 6 +- src/modules/tools/scaracal/SCARAcal.cpp | 32 +- src/modules/tools/spindle/Spindle.cpp | 2 +- src/modules/tools/switch/Switch.cpp | 4 + .../tools/temperaturecontrol/max31855.cpp | 4 +- .../predefined_thermistors.h | 2 +- src/modules/tools/toolmanager/Tool.h | 1 + .../tools/zprobe/DeltaCalibrationStrategy.cpp | 424 +++++++++++ .../tools/zprobe/DeltaCalibrationStrategy.h | 30 + .../tools/zprobe/DeltaGridStrategy.cpp | 595 +++++++++++++++ src/modules/tools/zprobe/DeltaGridStrategy.h | 50 ++ src/modules/tools/zprobe/LevelingStrategy.h | 24 + src/modules/tools/zprobe/Plane3D.cpp | 47 ++ src/modules/tools/zprobe/Plane3D.h | 23 + .../tools/zprobe/ThreePointStrategy.cpp | 399 ++++++++++ src/modules/tools/zprobe/ThreePointStrategy.h | 41 + src/modules/tools/zprobe/ZGridStrategy.cpp | 711 ++++++++++++++++++ src/modules/tools/zprobe/ZGridStrategy.h | 66 ++ src/modules/tools/zprobe/ZProbe.cpp | 531 +++++++++++++ src/modules/tools/zprobe/ZProbe.h | 75 ++ .../drivers/TMC26X/TMC26X.cpp | 1 - 48 files changed, 3227 insertions(+), 212 deletions(-) create mode 100644 src/modules/tools/zprobe/DeltaCalibrationStrategy.cpp create mode 100644 src/modules/tools/zprobe/DeltaCalibrationStrategy.h create mode 100644 src/modules/tools/zprobe/DeltaGridStrategy.cpp create mode 100644 src/modules/tools/zprobe/DeltaGridStrategy.h create mode 100644 src/modules/tools/zprobe/LevelingStrategy.h create mode 100644 src/modules/tools/zprobe/Plane3D.cpp create mode 100644 src/modules/tools/zprobe/Plane3D.h create mode 100644 src/modules/tools/zprobe/ThreePointStrategy.cpp create mode 100644 src/modules/tools/zprobe/ThreePointStrategy.h create mode 100644 src/modules/tools/zprobe/ZGridStrategy.cpp create mode 100644 src/modules/tools/zprobe/ZGridStrategy.h create mode 100644 src/modules/tools/zprobe/ZProbe.cpp create mode 100644 src/modules/tools/zprobe/ZProbe.h diff --git a/gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h b/gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h index c8cf3b6f..3269c08a 100644 --- a/gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h +++ b/gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h @@ -18,7 +18,8 @@ #include "device.h" -#if DEVICE_SLEEP +//#if DEVICE_SLEEP //TODO THIS WAS THE ORIGINAL BUT CONFLICTS WITH OTHER DEFINITION OF SLEEP +#ifndef DEVICE_SLEEP #ifdef __cplusplus extern "C" { diff --git a/makefile b/makefile index 3fbd2885..47f8f6bb 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ -#DIRS = gcc4mbed src -DIRS = src +DIRS = gcc4mbed src +#DIRS = src DIRSCLEAN = $(addsuffix .clean,$(DIRS)) all: diff --git a/src/libs/Hook.h b/src/libs/Hook.h index 2dd5203b..69d10ec6 100644 --- a/src/libs/Hook.h +++ b/src/libs/Hook.h @@ -7,8 +7,8 @@ class Hook : public FPointer { public: Hook(); - double interval; - double countdown; -}; // TOADDBACK :Ā doubles were ints + int interval; + int countdown; +}; #endif diff --git a/src/modules/communication/GcodeDispatch.cpp b/src/modules/communication/GcodeDispatch.cpp index 0af41317..9eb80e5d 100644 --- a/src/modules/communication/GcodeDispatch.cpp +++ b/src/modules/communication/GcodeDispatch.cpp @@ -360,6 +360,10 @@ void GcodeDispatch::on_console_line_received(void *line) new_message.stream->printf("unknown\r\n"); } + // we cannot continue safely after an error so we enter HALT state + new_message.stream->printf("Entering Alarm/Halt state\n"); + THEKERNEL->call_event(ON_HALT, nullptr); + }else{ if(gcode->add_nl) diff --git a/src/modules/communication/SerialConsole.cpp b/src/modules/communication/SerialConsole.cpp index 07171d5d..a9e1c7d7 100644 --- a/src/modules/communication/SerialConsole.cpp +++ b/src/modules/communication/SerialConsole.cpp @@ -20,8 +20,8 @@ using std::string; // Serial reading module // Treats every received line as a command and passes it ( via event call ) to the command dispatcher. // The command dispatcher will then ask other modules if they can do something with it -SerialConsole::SerialConsole( PinName tx_pin, PinName rx_pin, int baud_rate ){ - this->serial = new mbed::Serial( tx_pin, rx_pin ); +SerialConsole::SerialConsole( PinName rx_pin, PinName tx_pin, int baud_rate ){ + this->serial = new mbed::Serial( rx_pin, tx_pin ); this->serial->baud(baud_rate); } diff --git a/src/modules/communication/SerialConsole.h b/src/modules/communication/SerialConsole.h index 831099b2..49489db5 100644 --- a/src/modules/communication/SerialConsole.h +++ b/src/modules/communication/SerialConsole.h @@ -22,7 +22,7 @@ using std::string; class SerialConsole : public Module, public StreamOutput { public: - SerialConsole( PinName tx_pin, PinName rx_pin, int baud_rate ); + SerialConsole( PinName rx_pin, PinName tx_pin, int baud_rate ); void on_module_loaded(); void on_serial_char_received(); diff --git a/src/modules/robot/ActuatorCoordinates.h b/src/modules/robot/ActuatorCoordinates.h index f4c77fef..78763fd5 100644 --- a/src/modules/robot/ActuatorCoordinates.h +++ b/src/modules/robot/ActuatorCoordinates.h @@ -5,8 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef ACTUATOR_COORDINATES_H -#define ACTUATOR_COORDINATES_H +#pragma once + #include #ifndef MAX_ROBOT_ACTUATORS @@ -17,5 +17,3 @@ //Keep MAX_ROBOT_ACTUATORS as small as practical it impacts block size and therefore free memory. const size_t k_max_actuators = MAX_ROBOT_ACTUATORS; typedef struct std::array ActuatorCoordinates; - -#endif diff --git a/src/modules/robot/Robot.h b/src/modules/robot/Robot.h index e2a44577..51e61f81 100644 --- a/src/modules/robot/Robot.h +++ b/src/modules/robot/Robot.h @@ -143,10 +143,9 @@ class Robot : public Module { // computational efficiency of generating arcs. int arc_correction; // Setting : how often to rectify arc computation float max_speeds[3]; // Setting : max allowable speed in mm/s for each axis - - uint8_t selected_extruder; - uint8_t n_motors; //count + uint8_t selected_extruder; + uint8_t n_motors; //count of the motors/axis registered //used to initialize the TMC2130 stepper motor driver if present mbed::SPI *spi; diff --git a/src/modules/robot/arm_solutions/BaseSolution.h b/src/modules/robot/arm_solutions/BaseSolution.h index 92b2c811..76196c01 100644 --- a/src/modules/robot/arm_solutions/BaseSolution.h +++ b/src/modules/robot/arm_solutions/BaseSolution.h @@ -12,12 +12,11 @@ class BaseSolution { BaseSolution(){}; BaseSolution(Config*){}; virtual ~BaseSolution() {}; - virtual void cartesian_to_actuator(const float[], ActuatorCoordinates &) = 0; - virtual void actuator_to_cartesian(const ActuatorCoordinates &, float[]) = 0; + virtual void cartesian_to_actuator(const float[], ActuatorCoordinates &) const = 0; + virtual void actuator_to_cartesian(const ActuatorCoordinates &, float[]) const = 0; typedef std::map arm_options_t; virtual bool set_optional(const arm_options_t& options) { return false; }; - virtual bool get_optional(arm_options_t& options, bool force_all= false) { return false; }; - virtual size_t get_actuator_count() const { return 3; } + virtual bool get_optional(arm_options_t& options, bool force_all= false) const { return false; }; }; #endif diff --git a/src/modules/robot/arm_solutions/CartesianSolution.cpp b/src/modules/robot/arm_solutions/CartesianSolution.cpp index df2d901d..73fd4c51 100644 --- a/src/modules/robot/arm_solutions/CartesianSolution.cpp +++ b/src/modules/robot/arm_solutions/CartesianSolution.cpp @@ -2,13 +2,13 @@ #include "ActuatorCoordinates.h" #include -void CartesianSolution::cartesian_to_actuator( const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ +void CartesianSolution::cartesian_to_actuator( const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const { actuator_mm[ALPHA_STEPPER] = cartesian_mm[X_AXIS]; actuator_mm[BETA_STEPPER ] = cartesian_mm[Y_AXIS]; actuator_mm[GAMMA_STEPPER] = cartesian_mm[Z_AXIS]; } -void CartesianSolution::actuator_to_cartesian( const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ +void CartesianSolution::actuator_to_cartesian( const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const { cartesian_mm[ALPHA_STEPPER] = actuator_mm[X_AXIS]; cartesian_mm[BETA_STEPPER ] = actuator_mm[Y_AXIS]; cartesian_mm[GAMMA_STEPPER] = actuator_mm[Z_AXIS]; diff --git a/src/modules/robot/arm_solutions/CartesianSolution.h b/src/modules/robot/arm_solutions/CartesianSolution.h index 847f6a69..f1a84613 100644 --- a/src/modules/robot/arm_solutions/CartesianSolution.h +++ b/src/modules/robot/arm_solutions/CartesianSolution.h @@ -1,5 +1,5 @@ -#ifndef CARTESIANSOLUTION_H -#define CARTESIANSOLUTION_H +#pragma once + #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -11,13 +11,6 @@ class CartesianSolution : public BaseSolution { public: CartesianSolution(){}; CartesianSolution(Config*){}; - void cartesian_to_actuator( const float millimeters[], ActuatorCoordinates &steps ) override; - void actuator_to_cartesian( const ActuatorCoordinates &steps, float millimeters[] ) override; + void cartesian_to_actuator( const float millimeters[], ActuatorCoordinates &steps ) const override; + void actuator_to_cartesian( const ActuatorCoordinates &steps, float millimeters[] ) const override; }; - - - - - - -#endif diff --git a/src/modules/robot/arm_solutions/CoreXZSolution.cpp b/src/modules/robot/arm_solutions/CoreXZSolution.cpp index 01003f7e..4282e1ac 100644 --- a/src/modules/robot/arm_solutions/CoreXZSolution.cpp +++ b/src/modules/robot/arm_solutions/CoreXZSolution.cpp @@ -12,13 +12,13 @@ CoreXZSolution::CoreXZSolution(Config* config) z_reduction = config->value(z_reduction_checksum)->by_default(3.0f)->as_number(); } -void CoreXZSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ +void CoreXZSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const { actuator_mm[ALPHA_STEPPER] = (this->x_reduction * cartesian_mm[X_AXIS]) + (this->z_reduction * cartesian_mm[Z_AXIS]); actuator_mm[BETA_STEPPER ] = (this->x_reduction * cartesian_mm[X_AXIS]) - (this->z_reduction * cartesian_mm[Z_AXIS]); actuator_mm[GAMMA_STEPPER] = cartesian_mm[Y_AXIS]; } -void CoreXZSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ +void CoreXZSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const { cartesian_mm[X_AXIS] = (0.5F/this->x_reduction) * (actuator_mm[ALPHA_STEPPER] + actuator_mm[BETA_STEPPER]); cartesian_mm[Z_AXIS] = (0.5F/this->z_reduction) * (actuator_mm[ALPHA_STEPPER] - actuator_mm[BETA_STEPPER]); cartesian_mm[Y_AXIS] = actuator_mm[GAMMA_STEPPER]; diff --git a/src/modules/robot/arm_solutions/CoreXZSolution.h b/src/modules/robot/arm_solutions/CoreXZSolution.h index bca314f3..4b3d7995 100644 --- a/src/modules/robot/arm_solutions/CoreXZSolution.h +++ b/src/modules/robot/arm_solutions/CoreXZSolution.h @@ -1,5 +1,5 @@ -#ifndef COREXZSOLUTION_H -#define COREXZSOLUTION_H +#pragma once + #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -10,12 +10,10 @@ class CoreXZSolution : public BaseSolution { public: CoreXZSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates & ) override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; + void cartesian_to_actuator(const float[], ActuatorCoordinates & ) const override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; private: float x_reduction; float z_reduction; }; - -#endif // COREXZSOLUTION_H diff --git a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp index 69ad4ea3..2d7aed77 100644 --- a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp @@ -38,14 +38,15 @@ ExperimentalDeltaSolution::ExperimentalDeltaSolution(Config* config) arm_length_squared = powf(arm_length, 2); } -void ExperimentalDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ +void ExperimentalDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const +{ float alpha_rotated[3], rotated[3]; - if( sin_alpha == 0 && cos_alpha == 1){ + if( sin_alpha == 0 && cos_alpha == 1) { alpha_rotated[X_AXIS] = cartesian_mm[X_AXIS]; alpha_rotated[Y_AXIS] = cartesian_mm[Y_AXIS]; alpha_rotated[Z_AXIS] = cartesian_mm[Z_AXIS]; - }else{ + } else { rotate( cartesian_mm, alpha_rotated, sin_alpha, cos_alpha ); } actuator_mm[ALPHA_STEPPER] = solve_arm( alpha_rotated ); @@ -57,15 +58,18 @@ void ExperimentalDeltaSolution::cartesian_to_actuator(const float cartesian_mm[] actuator_mm[GAMMA_STEPPER] = solve_arm( rotated ); } -void ExperimentalDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ +void ExperimentalDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const +{ // unimplemented } -float ExperimentalDeltaSolution::solve_arm( float cartesian_mm[]) { +float ExperimentalDeltaSolution::solve_arm( float cartesian_mm[]) const +{ return sqrtf(arm_length_squared - powf(cartesian_mm[X_AXIS] - arm_radius, 2) - powf(cartesian_mm[Y_AXIS], 2)) + cartesian_mm[Z_AXIS]; } -void ExperimentalDeltaSolution::rotate(const float in[], float out[], float sin, float cos ){ +void ExperimentalDeltaSolution::rotate(const float in[], float out[], float sin, float cos ) const +{ out[X_AXIS] = cos * in[X_AXIS] - sin * in[Y_AXIS]; out[Y_AXIS] = sin * in[X_AXIS] + cos * in[Y_AXIS]; out[Z_AXIS] = in[Z_AXIS]; diff --git a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h index c888c3d3..16d38a86 100644 --- a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h +++ b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h @@ -1,5 +1,4 @@ -#ifndef EXPERIMENTALDELTASOLUTION_H -#define EXPERIMENTALDELTASOLUTION_H +#pragma once #include "BaseSolution.h" @@ -8,12 +7,12 @@ class Config; class ExperimentalDeltaSolution : public BaseSolution { public: ExperimentalDeltaSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; private: - float solve_arm( float millimeters[] ); - void rotate(const float in[], float out[], float sin, float cos ); + float solve_arm( float millimeters[] ) const ; + void rotate(const float in[], float out[], float sin, float cos ) const ; float arm_length; float arm_radius; @@ -26,10 +25,3 @@ class ExperimentalDeltaSolution : public BaseSolution { float sin_gamma; float cos_gamma; }; - - - - - - -#endif // EXPERIMENTALDELTASOLUTION_H diff --git a/src/modules/robot/arm_solutions/HBotSolution.cpp b/src/modules/robot/arm_solutions/HBotSolution.cpp index 70f56458..3a6655a7 100644 --- a/src/modules/robot/arm_solutions/HBotSolution.cpp +++ b/src/modules/robot/arm_solutions/HBotSolution.cpp @@ -2,13 +2,15 @@ #include "ActuatorCoordinates.h" #include -void HBotSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ +void HBotSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const +{ actuator_mm[ALPHA_STEPPER] = cartesian_mm[X_AXIS] + cartesian_mm[Y_AXIS]; actuator_mm[BETA_STEPPER ] = cartesian_mm[X_AXIS] - cartesian_mm[Y_AXIS]; actuator_mm[GAMMA_STEPPER] = cartesian_mm[Z_AXIS]; } -void HBotSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ +void HBotSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const +{ cartesian_mm[X_AXIS] = 0.5F * (actuator_mm[ALPHA_STEPPER] + actuator_mm[BETA_STEPPER]); cartesian_mm[Y_AXIS] = 0.5F * (actuator_mm[ALPHA_STEPPER] - actuator_mm[BETA_STEPPER]); cartesian_mm[Z_AXIS] = actuator_mm[GAMMA_STEPPER]; diff --git a/src/modules/robot/arm_solutions/HBotSolution.h b/src/modules/robot/arm_solutions/HBotSolution.h index c610f992..6b4281d6 100644 --- a/src/modules/robot/arm_solutions/HBotSolution.h +++ b/src/modules/robot/arm_solutions/HBotSolution.h @@ -1,5 +1,5 @@ -#ifndef HBOTSOLUTION_H -#define HBOTSOLUTION_H +#pragma once + #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -11,14 +11,6 @@ class HBotSolution : public BaseSolution { public: HBotSolution(); HBotSolution(Config*){}; - void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[]) override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[]) const override; }; - - - - - - -#endif // HBOTSOLUTION_H - diff --git a/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp b/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp index ad12c015..b0a16394 100644 --- a/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp @@ -41,10 +41,11 @@ LinearDeltaSolution::LinearDeltaSolution(Config* config) init(); } -void LinearDeltaSolution::init() { +void LinearDeltaSolution::init() +{ arm_length_squared = SQ(arm_length); - // Effective X/Y positions of the three vertical towers. + // Effective X/Y positions of the three vertical towers. float delta_radius = arm_radius; delta_tower1_x = (delta_radius + tower1_offset) * cosf((210.0F + tower1_angle) * PIOVER180); // front left tower @@ -55,7 +56,7 @@ void LinearDeltaSolution::init() { delta_tower3_y = (delta_radius + tower3_offset) * sinf((90.0F + tower3_angle) * PIOVER180); } -void LinearDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) +void LinearDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const { actuator_mm[ALPHA_STEPPER] = sqrtf(this->arm_length_squared @@ -72,7 +73,7 @@ void LinearDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu ) + cartesian_mm[Z_AXIS]; } -void LinearDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) +void LinearDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const { // from http://en.wikipedia.org/wiki/Circumscribed_circle#Barycentric_coordinates_from_cross-_and_dot-products // based on https://github.com/ambrop72/aprinter/blob/2de69a/aprinter/printer/DeltaTransform.h#L81 @@ -111,12 +112,13 @@ void LinearDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actua cartesian_mm[2] = ROUND(cartesian[2], 4); } -bool LinearDeltaSolution::set_optional(const arm_options_t& options) { +bool LinearDeltaSolution::set_optional(const arm_options_t& options) +{ for(auto &i : options) { switch(i.first) { - case 'L': arm_length= i.second; break; - case 'R': arm_radius= i.second; break; + case 'L': arm_length = i.second; break; + case 'R': arm_radius = i.second; break; case 'A': tower1_offset = i.second; break; case 'B': tower2_offset = i.second; break; case 'C': tower3_offset = i.second; break; @@ -130,13 +132,14 @@ bool LinearDeltaSolution::set_optional(const arm_options_t& options) { return true; } -bool LinearDeltaSolution::get_optional(arm_options_t& options, bool force_all) { - options['L']= this->arm_length; - options['R']= this->arm_radius; +bool LinearDeltaSolution::get_optional(arm_options_t& options, bool force_all) const +{ + options['L'] = this->arm_length; + options['R'] = this->arm_radius; // don't report these if none of them are set if(force_all || (this->tower1_offset != 0.0F || this->tower2_offset != 0.0F || this->tower3_offset != 0.0F || - this->tower1_angle != 0.0F || this->tower2_angle != 0.0F || this->tower3_angle != 0.0F) ) { + this->tower1_angle != 0.0F || this->tower2_angle != 0.0F || this->tower3_angle != 0.0F) ) { options['A'] = this->tower1_offset; options['B'] = this->tower2_offset; diff --git a/src/modules/robot/arm_solutions/LinearDeltaSolution.h b/src/modules/robot/arm_solutions/LinearDeltaSolution.h index b7ebd208..401b7189 100644 --- a/src/modules/robot/arm_solutions/LinearDeltaSolution.h +++ b/src/modules/robot/arm_solutions/LinearDeltaSolution.h @@ -1,5 +1,5 @@ -#ifndef LINEARDELTASOLUTION_H -#define LINEARDELTASOLUTION_H +#pragma once + #include "libs/Module.h" #include "BaseSolution.h" @@ -8,11 +8,11 @@ class Config; class LinearDeltaSolution : public BaseSolution { public: LinearDeltaSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; bool set_optional(const arm_options_t& options) override; - bool get_optional(arm_options_t& options, bool force_all) override; + bool get_optional(arm_options_t& options, bool force_all) const override; private: void init(); @@ -34,4 +34,3 @@ class LinearDeltaSolution : public BaseSolution { float tower2_angle; float tower3_angle; }; -#endif // LINEARDELTASOLUTION_H diff --git a/src/modules/robot/arm_solutions/MorganSCARASolution.cpp b/src/modules/robot/arm_solutions/MorganSCARASolution.cpp index 304da817..dee86c4d 100644 --- a/src/modules/robot/arm_solutions/MorganSCARASolution.cpp +++ b/src/modules/robot/arm_solutions/MorganSCARASolution.cpp @@ -51,15 +51,17 @@ MorganSCARASolution::MorganSCARASolution(Config* config) init(); } -void MorganSCARASolution::init() { +void MorganSCARASolution::init() +{ } -float MorganSCARASolution::to_degrees(float radians) { - return radians*(180.0F/3.14159265359f); +float MorganSCARASolution::to_degrees(float radians) const +{ + return radians * (180.0F / 3.14159265359f); } -void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) +void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const { float SCARA_pos[2], @@ -75,9 +77,9 @@ void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], Actu // Y has to be scaled before subtracting offset to ensure position on bed. if (this->arm1_length == this->arm2_length) - SCARA_C2 = (SQ(SCARA_pos[X_AXIS])+SQ(SCARA_pos[Y_AXIS])-2.0f*SQ(this->arm1_length)) / (2.0f * SQ(this->arm1_length)); + SCARA_C2 = (SQ(SCARA_pos[X_AXIS]) + SQ(SCARA_pos[Y_AXIS]) - 2.0f * SQ(this->arm1_length)) / (2.0f * SQ(this->arm1_length)); else - SCARA_C2 = (SQ(SCARA_pos[X_AXIS])+SQ(SCARA_pos[Y_AXIS])-SQ(this->arm1_length)-SQ(this->arm2_length)) / (2.0f * SQ(this->arm1_length)); + SCARA_C2 = (SQ(SCARA_pos[X_AXIS]) + SQ(SCARA_pos[Y_AXIS]) - SQ(this->arm1_length) - SQ(this->arm2_length)) / (2.0f * SQ(this->arm1_length)); // SCARA position is undefined if abs(SCARA_C2) >=1 // In reality abs(SCARA_C2) >0.95 can be problematic. @@ -88,13 +90,13 @@ void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], Actu SCARA_C2 = -this->morgan_undefined_min; - SCARA_S2 = sqrtf(1.0f-SQ(SCARA_C2)); + SCARA_S2 = sqrtf(1.0f - SQ(SCARA_C2)); - SCARA_K1 = this->arm1_length+this->arm2_length*SCARA_C2; - SCARA_K2 = this->arm2_length*SCARA_S2; + SCARA_K1 = this->arm1_length + this->arm2_length * SCARA_C2; + SCARA_K2 = this->arm2_length * SCARA_S2; - SCARA_theta = (atan2f(SCARA_pos[X_AXIS],SCARA_pos[Y_AXIS])-atan2f(SCARA_K1, SCARA_K2))*-1.0f; // Morgan Thomas turns Theta in oposite direction - SCARA_psi = atan2f(SCARA_S2,SCARA_C2); + SCARA_theta = (atan2f(SCARA_pos[X_AXIS], SCARA_pos[Y_AXIS]) - atan2f(SCARA_K1, SCARA_K2)) * -1.0f; // Morgan Thomas turns Theta in oposite direction + SCARA_psi = atan2f(SCARA_S2, SCARA_C2); actuator_mm[ALPHA_STEPPER] = to_degrees(SCARA_theta); // Multiply by 180/Pi - theta is support arm angle @@ -104,19 +106,20 @@ void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], Actu } -void MorganSCARASolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) { +void MorganSCARASolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const +{ // Perform forward kinematics, and place results in cartesian_mm[] float y1, y2, - actuator_rad[2]; + actuator_rad[2]; - actuator_rad[X_AXIS] = actuator_mm[X_AXIS]/(180.0F/3.14159265359f); - actuator_rad[Y_AXIS] = actuator_mm[Y_AXIS]/(180.0F/3.14159265359f); + actuator_rad[X_AXIS] = actuator_mm[X_AXIS] / (180.0F / 3.14159265359f); + actuator_rad[Y_AXIS] = actuator_mm[Y_AXIS] / (180.0F / 3.14159265359f); - y1 = sinf(actuator_rad[X_AXIS])*this->arm1_length; - y2 = sinf(actuator_rad[Y_AXIS])*this->arm2_length + y1; + y1 = sinf(actuator_rad[X_AXIS]) * this->arm1_length; + y2 = sinf(actuator_rad[Y_AXIS]) * this->arm2_length + y1; - cartesian_mm[X_AXIS] = (((cosf(actuator_rad[X_AXIS])*this->arm1_length) + (cosf(actuator_rad[Y_AXIS])*this->arm2_length)) / this->morgan_scaling_x) + this->morgan_offset_x; + cartesian_mm[X_AXIS] = (((cosf(actuator_rad[X_AXIS]) * this->arm1_length) + (cosf(actuator_rad[Y_AXIS]) * this->arm2_length)) / this->morgan_scaling_x) + this->morgan_offset_x; cartesian_mm[Y_AXIS] = (y2 + this->morgan_offset_y) / this->morgan_scaling_y; cartesian_mm[Z_AXIS] = actuator_mm[Z_AXIS]; @@ -125,44 +128,45 @@ void MorganSCARASolution::actuator_to_cartesian(const ActuatorCoordinates &actua cartesian_mm[2] = ROUND(cartesian_mm[2], 7); } -bool MorganSCARASolution::set_optional(const arm_options_t& options) { +bool MorganSCARASolution::set_optional(const arm_options_t& options) +{ arm_options_t::const_iterator i; - i= options.find('T'); // Theta arm1 length + i = options.find('T'); // Theta arm1 length if(i != options.end()) { - arm1_length= i->second; + arm1_length = i->second; } - i= options.find('P'); // Psi arm2 length + i = options.find('P'); // Psi arm2 length if(i != options.end()) { - arm2_length= i->second; + arm2_length = i->second; } - i= options.find('X'); // Home initial position X + i = options.find('X'); // Home initial position X if(i != options.end()) { - morgan_offset_x= i->second; + morgan_offset_x = i->second; } - i= options.find('Y'); // Home initial position Y + i = options.find('Y'); // Home initial position Y if(i != options.end()) { - morgan_offset_y= i->second; + morgan_offset_y = i->second; } - i= options.find('A'); // Scaling X_AXIS + i = options.find('A'); // Scaling X_AXIS if(i != options.end()) { - morgan_scaling_x= i->second; + morgan_scaling_x = i->second; } - i= options.find('B'); // Scaling Y_AXIS + i = options.find('B'); // Scaling Y_AXIS if(i != options.end()) { - morgan_scaling_y= i->second; + morgan_scaling_y = i->second; } //i= options.find('C'); // Scaling Z_AXIS //if(i != options.end()) { // morgan_scaling_z= i->second; //} - i= options.find('D'); // Undefined min + i = options.find('D'); // Undefined min if(i != options.end()) { this->morgan_undefined_min = i->second; } - i= options.find('E'); // undefined max + i = options.find('E'); // undefined max if(i != options.end()) { this->morgan_undefined_max = i->second; } @@ -171,16 +175,17 @@ bool MorganSCARASolution::set_optional(const arm_options_t& options) { return true; } -bool MorganSCARASolution::get_optional(arm_options_t& options, bool force_all) { - options['T']= this->arm1_length; - options['P']= this->arm2_length; - options['X']= this->morgan_offset_x; - options['Y']= this->morgan_offset_y; - options['A']= this->morgan_scaling_x; - options['B']= this->morgan_scaling_y; +bool MorganSCARASolution::get_optional(arm_options_t& options, bool force_all) const +{ + options['T'] = this->arm1_length; + options['P'] = this->arm2_length; + options['X'] = this->morgan_offset_x; + options['Y'] = this->morgan_offset_y; + options['A'] = this->morgan_scaling_x; + options['B'] = this->morgan_scaling_y; // options['C']= this->morgan_scaling_z; - options['D']= this->morgan_undefined_min; - options['E']= this->morgan_undefined_max; + options['D'] = this->morgan_undefined_min; + options['E'] = this->morgan_undefined_max; return true; }; diff --git a/src/modules/robot/arm_solutions/MorganSCARASolution.h b/src/modules/robot/arm_solutions/MorganSCARASolution.h index 4606c234..1ab897aa 100644 --- a/src/modules/robot/arm_solutions/MorganSCARASolution.h +++ b/src/modules/robot/arm_solutions/MorganSCARASolution.h @@ -8,15 +8,15 @@ class Config; class MorganSCARASolution : public BaseSolution { public: MorganSCARASolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; bool set_optional(const arm_options_t& options) override; - bool get_optional(arm_options_t& options, bool force_all) override; + bool get_optional(arm_options_t& options, bool force_all) const override; private: void init(); - float to_degrees(float radians); + float to_degrees(float radians) const; float arm1_length; float arm2_length; diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp index 284d1a09..c7af9131 100644 --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp @@ -22,6 +22,7 @@ #define delta_mirror_xy_checksum CHECKSUM("delta_mirror_xy") const static float pi = 3.14159265358979323846; // PI +const static float two_pi = 2 * pi; const static float sin120 = 0.86602540378443864676372317075294; //sqrt3/2.0 const static float cos120 = -0.5; const static float tan60 = 1.7320508075688772935274463415059; //sqrt3; @@ -61,7 +62,7 @@ RotaryDeltaSolution::RotaryDeltaSolution(Config *config) // inverse kinematics // helper functions, calculates angle theta1 (for YZ-pane) -int RotaryDeltaSolution::delta_calcAngleYZ(float x0, float y0, float z0, float &theta) +int RotaryDeltaSolution::delta_calcAngleYZ(float x0, float y0, float z0, float &theta) const { float y1 = -0.5F * tan30 * delta_f; // f/2 * tan 30 y0 -= 0.5F * tan30 * delta_e; // shift center to edge @@ -81,7 +82,7 @@ int RotaryDeltaSolution::delta_calcAngleYZ(float x0, float y0, float z0, float & // forward kinematics: (theta1, theta2, theta3) -> (x0, y0, z0) // returned status: 0=OK, -1=non-existing position -int RotaryDeltaSolution::delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0) +int RotaryDeltaSolution::delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0) const { float t = (delta_f - delta_e) * tan30 / 2.0F; float degrees_to_radians = pi / 180.0F; @@ -139,7 +140,7 @@ void RotaryDeltaSolution::init() z_calc_offset = -(delta_z_offset - tool_offset - delta_ee_offs); } -void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) +void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const { //We need to translate the Cartesian coordinates in mm to the actuator position required in mm so the stepper motor functions float alpha_theta = 0.0F; @@ -195,7 +196,7 @@ void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu } -void RotaryDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) +void RotaryDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const { float x, y, z; //Use forward kinematics @@ -230,7 +231,7 @@ bool RotaryDeltaSolution::set_optional(const arm_options_t &options) return true; } -bool RotaryDeltaSolution::get_optional(arm_options_t &options, bool force_all) +bool RotaryDeltaSolution::get_optional(arm_options_t &options, bool force_all) const { options['A'] = this->delta_e; options['B'] = this->delta_f; diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.h b/src/modules/robot/arm_solutions/RotaryDeltaSolution.h index 52334fab..ecb9e4c0 100644 --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.h +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.h @@ -1,5 +1,5 @@ -#ifndef RotaryDeltaSolution_H -#define RotaryDeltaSolution_H +#pragma once + #include "libs/Module.h" #include "BaseSolution.h" @@ -8,16 +8,16 @@ class Config; class RotaryDeltaSolution : public BaseSolution { public: RotaryDeltaSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; bool set_optional(const arm_options_t& options) override; - bool get_optional(arm_options_t& options, bool force_all) override; + bool get_optional(arm_options_t& options, bool force_all) const override; private: void init(); - int delta_calcAngleYZ(float x0, float y0, float z0, float &theta); - int delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0); + int delta_calcAngleYZ(float x0, float y0, float z0, float &theta) const; + int delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0) const; float delta_e; // End effector length float delta_f; // Base length @@ -35,4 +35,3 @@ class RotaryDeltaSolution : public BaseSolution { bool mirror_xy:1; }; }; -#endif // RotaryDeltaSolution_H diff --git a/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp b/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp index 097e3fdc..8797a2fc 100644 --- a/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp +++ b/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp @@ -7,21 +7,25 @@ // degrees * (pi / 180) = radians #define DEG2RAD 0.01745329251994329576923690768489 -RotatableCartesianSolution::RotatableCartesianSolution(Config* config) { +RotatableCartesianSolution::RotatableCartesianSolution(Config* config) +{ float alpha_angle = config->value(alpha_angle_checksum)->by_default(0.0f)->as_number() * DEG2RAD; sin_alpha = sinf(alpha_angle); cos_alpha = cosf(alpha_angle); } -void RotatableCartesianSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ +void RotatableCartesianSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const +{ rotate( cartesian_mm, &actuator_mm[0], sin_alpha, cos_alpha ); } -void RotatableCartesianSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ +void RotatableCartesianSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const +{ rotate( &actuator_mm[0], cartesian_mm, - sin_alpha, cos_alpha ); } -void RotatableCartesianSolution::rotate(const float in[], float out[], float sin, float cos ){ +void RotatableCartesianSolution::rotate(const float in[], float out[], float sin, float cos ) const +{ out[ALPHA_STEPPER] = cos * in[X_AXIS] - sin * in[Y_AXIS]; out[BETA_STEPPER ] = sin * in[X_AXIS] + cos * in[Y_AXIS]; out[GAMMA_STEPPER] = in[Z_AXIS]; diff --git a/src/modules/robot/arm_solutions/RotatableCartesianSolution.h b/src/modules/robot/arm_solutions/RotatableCartesianSolution.h index 519affb7..3e05dab8 100644 --- a/src/modules/robot/arm_solutions/RotatableCartesianSolution.h +++ b/src/modules/robot/arm_solutions/RotatableCartesianSolution.h @@ -1,5 +1,5 @@ -#ifndef ROTATABLECARTESIANSOLUTION_H -#define ROTATABLECARTESIANSOLUTION_H +#pragma once + #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -12,16 +12,12 @@ class RotatableCartesianSolution : public BaseSolution { public: RotatableCartesianSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; private: - void rotate(const float in[], float out[], float sin, float cos); + void rotate(const float in[], float out[], float sin, float cos) const; float sin_alpha; float cos_alpha; }; - - -#endif // ROTATABLECARTESIANSOLUTION_H - diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index 3ec9055a..c36611bc 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -29,7 +29,6 @@ #include "StepTicker.h" #include "BaseSolution.h" #include "SerialMessage.h" -#include "SEGGER_SYSVIEW.h" #include @@ -653,6 +652,16 @@ void Endstops::process_home_command(Gcode* gcode) if(THEKERNEL->is_halted()) break; } + } else if(is_corexy) { + // corexy must home each axis individually + for (int a = X_AXIS; a <= Z_AXIS; ++a) { + if(haxis[a]) { + std::bitset<3> bs; + bs.set(a); + home(bs); + } + } + } else { // they could all home at the same time home(haxis); diff --git a/src/modules/tools/extruder/Extruder.cpp b/src/modules/tools/extruder/Extruder.cpp index df29091a..5659761a 100644 --- a/src/modules/tools/extruder/Extruder.cpp +++ b/src/modules/tools/extruder/Extruder.cpp @@ -14,7 +14,6 @@ #include "modules/robot/Block.h" #include "StepperMotor.h" #include "SlowTicker.h" -#include "StepTicker.h" #include "Config.h" #include "StepperMotor.h" #include "Robot.h" @@ -25,7 +24,6 @@ #include "PublicDataRequest.h" #include "StreamOutputPool.h" #include "ExtruderPublicAccess.h" -#include "nuts_bolts.h" #include @@ -109,7 +107,7 @@ void Extruder::config_load() en_pin.from_string( THEKERNEL->config->value(extruder_checksum, this->identifier, en_pin_checksum )->by_default("nc" )->as_string())->as_output(); float steps_per_millimeter = THEKERNEL->config->value(extruder_checksum, this->identifier, steps_per_mm_checksum)->by_default(1)->as_number(); -//TODO Is this still needed float acceleration = THEKERNEL->config->value(extruder_checksum, this->identifier, acceleration_checksum)->by_default(1000)->as_number(); + float acceleration = THEKERNEL->config->value(extruder_checksum, this->identifier, acceleration_checksum)->by_default(1000)->as_number(); this->offset[X_AXIS] = THEKERNEL->config->value(extruder_checksum, this->identifier, x_offset_checksum )->by_default(0)->as_number(); this->offset[Y_AXIS] = THEKERNEL->config->value(extruder_checksum, this->identifier, y_offset_checksum )->by_default(0)->as_number(); @@ -129,28 +127,27 @@ void Extruder::config_load() // Stepper motor object for the extruder stepper_motor = new StepperMotor(step_pin, dir_pin, en_pin); - //motor_id = THEROBOT->register_motor(stepper_motor); - motor_id = THEKERNEL->step_ticker->register_motor(stepper_motor); + motor_id = THEROBOT->register_motor(stepper_motor); stepper_motor->set_max_rate(THEKERNEL->config->value(extruder_checksum, this->identifier, max_speed_checksum)->by_default(1000)->as_number()); -//TODO is this still required stepper_motor->set_acceleration(acceleration); + stepper_motor->set_acceleration(acceleration); stepper_motor->change_steps_per_mm(steps_per_millimeter); -//TODO is this still required stepper_motor->set_selected(false); // not selected by default + stepper_motor->set_selected(false); // not selected by default } void Extruder::select() { selected = true; -//TODO is this still required stepper_motor->set_selected(true); + stepper_motor->set_selected(true); // set the function pointer to return the current scaling -//TODO is this still required THEROBOT->get_e_scale_fnc = std::bind(&Extruder::get_e_scale, this); + THEROBOT->get_e_scale_fnc = std::bind(&Extruder::get_e_scale, this); } void Extruder::deselect() { selected = false; -//TODO is this still required stepper_motor->set_selected(false); -//TODO is this still required THEROBOT->get_e_scale_fnc = nullptr; + stepper_motor->set_selected(false); + THEROBOT->get_e_scale_fnc = nullptr; } void Extruder::on_get_public_data(void *argument) @@ -166,7 +163,7 @@ void Extruder::on_get_public_data(void *argument) e->steps_per_mm = stepper_motor->get_steps_per_mm(); e->filament_diameter = this->filament_diameter; e->flow_rate = this->extruder_multiplier; -//TODO is this still required e->accleration = stepper_motor->get_acceleration(); + e->accleration = stepper_motor->get_acceleration(); e->retract_length = this->retract_length; e->current_position = stepper_motor->get_current_position(); pdr->set_taken(); @@ -210,13 +207,13 @@ void Extruder::on_set_public_data(void *argument) void Extruder::save_position() { // we need to save these separately as they may have been scaled -//TODO is this still required this->saved_position = std::make_tuple(THEKERNEL->robot->get_axis_position(motor_id), stepper_motor->get_last_milestone(), stepper_motor->get_last_milestone_steps()); + this->saved_position = std::make_tuple(THEROBOT->get_axis_position(motor_id), stepper_motor->get_last_milestone(), stepper_motor->get_last_milestone_steps()); } void Extruder::restore_position() { - //TODO is this still required THEKERNEL->robot->->reset_axis_position(std::get<0>(this->saved_position), motor_id); -//TODO is this still required stepper_motor->set_last_milestones(std::get<1>(this->saved_position), std::get<2>(this->saved_position)); + THEROBOT->reset_axis_position(std::get<0>(this->saved_position), motor_id); + stepper_motor->set_last_milestones(std::get<1>(this->saved_position), std::get<2>(this->saved_position)); } // check against maximum speeds and return the rate modifier @@ -247,9 +244,9 @@ void Extruder::on_gcode_received(void *argument) if (gcode->m == 114 && this->selected) { char buf[16]; if(gcode->subcode == 0) { -//TODO ADDBACK float pos = THEKERNEL->robot->get_axis_position(motor_id); -//TODO ADDBACK int n = snprintf(buf, sizeof(buf), " E:%1.3f ", pos); -//TODO ADDBACK gcode->txt_after_ok.append(buf, n); + float pos = THEROBOT->get_axis_position(motor_id); + int n = snprintf(buf, sizeof(buf), " E:%1.3f ", pos); + gcode->txt_after_ok.append(buf, n); } else if(gcode->subcode == 1) { // realtime position int n = snprintf(buf, sizeof(buf), " E:%1.3f ", stepper_motor->get_current_position() / get_e_scale()); @@ -313,7 +310,7 @@ void Extruder::on_gcode_received(void *argument) } else if (gcode->m == 204 && gcode->has_letter('E') && ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier)) ) { // extruder acceleration M204 Ennn mm/sec^2 (Pnnn sets the specific extruder for M500) -//TODO is this still required stepper_motor->set_acceleration(gcode->get_value('E')); + stepper_motor->set_acceleration(gcode->get_value('E')); } else if (gcode->m == 207 && ( (this->selected && !gcode->has_letter('P')) || (gcode->has_letter('P') && gcode->get_value('P') == this->identifier)) ) { // M207 - set retract length S[positive mm] F[feedrate mm/min] Z[additional zlift/hop] Q[zlift feedrate mm/min] @@ -347,7 +344,7 @@ void Extruder::on_gcode_received(void *argument) gcode->stream->printf(";E Filament diameter:\nM200 D%1.4f P%d\n", this->filament_diameter, this->identifier); gcode->stream->printf(";E retract length, feedrate:\nM207 S%1.4f F%1.4f Z%1.4f Q%1.4f P%d\n", this->retract_length, this->retract_feedrate * 60.0F, this->retract_zlift_length, this->retract_zlift_feedrate * 60.0F, this->identifier); gcode->stream->printf(";E retract recover length, feedrate:\nM208 S%1.4f F%1.4f P%d\n", this->retract_recover_length, this->retract_recover_feedrate * 60.0F, this->identifier); -//TODO is this still required gcode->stream->printf(";E acceleration mm/sec²:\nM204 E%1.4f P%d\n", stepper_motor->get_acceleration(), this->identifier); + gcode->stream->printf(";E acceleration mm/sec²:\nM204 E%1.4f P%d\n", stepper_motor->get_acceleration(), this->identifier); gcode->stream->printf(";E max feed rate mm/sec:\nM203 E%1.4f P%d\n", stepper_motor->get_max_rate(), this->identifier); if(this->max_volumetric_rate > 0) { gcode->stream->printf(";E max volumetric rate mm³/sec:\nM203 V%1.4f P%d\n", this->max_volumetric_rate, this->identifier); @@ -376,12 +373,12 @@ void Extruder::on_gcode_received(void *argument) } delta[motor_id] = -retract_length / get_e_scale(); // convert from mm to mm³, and unapply flow_rate - THEKERNEL->robot->delta_move(delta, retract_feedrate, motor_id + 1); + THEROBOT->delta_move(delta, retract_feedrate, motor_id + 1); // zlift if(retract_zlift_length > 0) { float delta[3] {0, 0, retract_zlift_length}; - THEKERNEL->robot->delta_move(delta, retract_zlift_feedrate, 3); + THEROBOT->delta_move(delta, retract_zlift_feedrate, 3); } } else if(gcode->g == 11) { @@ -390,7 +387,7 @@ void Extruder::on_gcode_received(void *argument) // reverse zlift happens before unretract // NOTE we do not do this if cancel_zlift_restore is set to true, which happens if there is an absolute Z move inbetween G10 and G11 float delta[3] {0, 0, -retract_zlift_length}; - THEKERNEL->robot->delta_move(delta, retract_zlift_feedrate, 3); + THEROBOT->delta_move(delta, retract_zlift_feedrate, 3); } float delta[motor_id + 1]; @@ -403,7 +400,7 @@ void Extruder::on_gcode_received(void *argument) // however this is not fatal for an extruder if(g92e0_detected) save_position(); delta[motor_id] = (retract_length + retract_recover_length) / get_e_scale(); // convert from mm to mm³, and unapply flow_rate - THEKERNEL->robot->delta_move(delta, retract_recover_feedrate, motor_id + 1); + THEROBOT->delta_move(delta, retract_recover_feedrate, motor_id + 1); if(g92e0_detected) restore_position(); } diff --git a/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp b/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp index 70e1dc3c..738fcf62 100644 --- a/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp +++ b/src/modules/tools/rotarydeltacalibration/RotaryDeltaCalibration.cpp @@ -65,14 +65,14 @@ void RotaryDeltaCalibration::on_gcode_received(void *argument) ActuatorCoordinates current_angle; // get the current angle for each actuator, NOTE we only deal with ABC so if there are more than 3 actuators this will probably go wonky for (size_t i = 0; i < 3; i++) { - current_angle[i]= THEKERNEL->robot->actuators[i]->get_current_position(); + current_angle[i]= THEROBOT->actuators[i]->get_current_position(); } if (gcode->has_letter('L') && gcode->get_value('L') != 0) { // specifying L1 it will take the last probe position (set by G30 or G38.x ) and set the home offset based on that // this allows the use of G30 to find the angle tool uint8_t ok; - std::tie(current_angle[0], current_angle[1], current_angle[2], ok) = THEKERNEL->robot->get_last_probe_position(); + std::tie(current_angle[0], current_angle[1], current_angle[2], ok) = THEROBOT->get_last_probe_position(); if(ok == 0) { gcode->stream->printf("error:Nothing set as probe failed or not run\n"); return; @@ -110,7 +110,7 @@ void RotaryDeltaCalibration::on_gcode_received(void *argument) // reset the actuator positions (and machine position accordingly) // But only if all three actuators have been specified at the same time if(cnt == 3 || (gcode->has_letter('R') && gcode->get_value('R') != 0)) { - THEKERNEL->robot->reset_actuator_position(current_angle); + THEROBOT->reset_actuator_position(current_angle); gcode->stream->printf("NOTE: actuator position reset\n"); }else{ gcode->stream->printf("NOTE: actuator position NOT reset\n"); diff --git a/src/modules/tools/scaracal/SCARAcal.cpp b/src/modules/tools/scaracal/SCARAcal.cpp index 326eedb1..8b536adf 100644 --- a/src/modules/tools/scaracal/SCARAcal.cpp +++ b/src/modules/tools/scaracal/SCARAcal.cpp @@ -32,7 +32,7 @@ #define Y_AXIS 1 #define Z_AXIS 2 -#define STEPPER THEKERNEL->robot->actuators +#define STEPPER THEROBOT->actuators #define STEPS_PER_MM(a) (STEPPER[a]->get_steps_per_mm()) void SCARAcal::on_module_loaded() @@ -135,7 +135,7 @@ bool SCARAcal::translate_trim(StreamOutput *stream) this->get_home_offset(home_off[0], home_off[1], home_off[2]); // get home offsets this->get_trim(S_trim[0], S_trim[1], S_trim[2]); // get current trim - THEKERNEL->robot->arm_solution->cartesian_to_actuator( home_off, actuator ); // convert current home offset to actuator angles + THEROBOT->arm_solution->cartesian_to_actuator( home_off, actuator ); // convert current home offset to actuator angles // Subtract trim values from actuators to determine the real home offset actuator position for X and Y. @@ -148,7 +148,7 @@ bool SCARAcal::translate_trim(StreamOutput *stream) // convert back to get the real cartesian home offsets - THEKERNEL->robot->arm_solution->actuator_to_cartesian( actuator, home_off ); + THEROBOT->arm_solution->actuator_to_cartesian( actuator, home_off ); this->set_home_offset(home_off[0], home_off[1], home_off[2],stream); // get home offsets // Set the correct home offsets; @@ -170,7 +170,7 @@ void SCARAcal::SCARA_ang_move(float theta, float psi, float z, float feedrate) actuator[2] = z; // Calculate the physical position relating to the arm angles - THEKERNEL->robot->arm_solution->actuator_to_cartesian( actuator, cartesian ); + THEROBOT->arm_solution->actuator_to_cartesian( actuator, cartesian ); // Assemble Gcode to add onto the queue snprintf(cmd, sizeof(cmd), "G0 X%1.3f Y%1.3f Z%1.3f F%1.1f", cartesian[0], cartesian[1], cartesian[2], feedrate * 60); // use specified feedrate (mm/sec) @@ -178,7 +178,7 @@ void SCARAcal::SCARA_ang_move(float theta, float psi, float z, float feedrate) //THEKERNEL->streams->printf("DEBUG: move: %s\n", cmd); Gcode gc(cmd, &(StreamOutput::NullStream)); - THEKERNEL->robot->on_gcode_received(&gc); // send to robot directly + THEROBOT->on_gcode_received(&gc); // send to robot directly } //A GCode has been received @@ -195,8 +195,8 @@ void SCARAcal::on_gcode_received(void *argument) float cartesian[3]; ActuatorCoordinates actuators; - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot - THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position + THEROBOT->get_axis_position(cartesian); // get actual position from robot + THEROBOT->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position int n = snprintf(buf, sizeof(buf), " A: Th:%1.3f Ps:%1.3f", actuators[0], @@ -218,8 +218,8 @@ void SCARAcal::on_gcode_received(void *argument) float S_delta[2], S_trim[3]; - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot - THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position + THEROBOT->get_axis_position(cartesian); // get actual position from robot + THEROBOT->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position S_delta[0] = actuators[0] - target[0]; @@ -227,7 +227,7 @@ void SCARAcal::on_gcode_received(void *argument) } else { set_trim(0, S_trim[1], S_trim[2], gcode->stream); // reset trim for calibration move this->home(); // home - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEROBOT->get_axis_position(cartesian); // get actual position from robot SCARA_ang_move(target[0], target[1], cartesian[2] + this->z_move, slow_rate * 3.0F); // move to target } } @@ -241,14 +241,14 @@ void SCARAcal::on_gcode_received(void *argument) // Program the current position as target ActuatorCoordinates actuators; - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot - THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position + THEROBOT->get_axis_position(cartesian); // get actual position from robot + THEROBOT->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate to get actuator position STEPPER[0]->change_steps_per_mm(actuators[0] / target[0] * STEPPER[0]->get_steps_per_mm()); // Find angle difference STEPPER[1]->change_steps_per_mm(STEPPER[0]->get_steps_per_mm()); // and change steps_per_mm to ensure correct steps per *angle* } else { this->home(); // home - This time leave trims as adjusted. - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEROBOT->get_axis_position(cartesian); // get actual position from robot SCARA_ang_move(target[0], target[1], cartesian[2] + this->z_move, slow_rate * 3.0F); // move to target } @@ -267,15 +267,15 @@ void SCARAcal::on_gcode_received(void *argument) ActuatorCoordinates actuators; float S_delta[2]; - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot - THEKERNEL->robot->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate it to get actual actuator angles + THEROBOT->get_axis_position(cartesian); // get actual position from robot + THEROBOT->arm_solution->cartesian_to_actuator( cartesian, actuators ); // translate it to get actual actuator angles S_delta[1] = ( actuators[1] - actuators[0]) - ( target[1] - target[0] ); // Find difference in angle - not actuator difference, and set_trim(S_trim[0], S_delta[1], S_trim[2], gcode->stream); // set trim to reflect the difference } else { set_trim(S_trim[0], 0, S_trim[2], gcode->stream); // reset trim for calibration move this->home(); // home - THEKERNEL->robot->get_axis_position(cartesian); // get actual position from robot + THEROBOT->get_axis_position(cartesian); // get actual position from robot SCARA_ang_move(target[0], target[1], cartesian[2] + this->z_move, slow_rate * 3.0F); // move to target } } diff --git a/src/modules/tools/spindle/Spindle.cpp b/src/modules/tools/spindle/Spindle.cpp index b8e12945..879b8eec 100644 --- a/src/modules/tools/spindle/Spindle.cpp +++ b/src/modules/tools/spindle/Spindle.cpp @@ -97,7 +97,7 @@ void Spindle::on_module_loaded() PinName pinname = port_pin((PortName)smoothie_pin->port_number, smoothie_pin->pin_number); feedback_pin = new InterruptIn(pinname); //feedback_pin.rise(&on_pin_rise); // TODO need to assign interrupt routine - //NVIC_SetPriority(EINT3_IRQn, 16); //TODO Need to set to low priority + //NVIC_SetPriority(EINT3_IRQn, 16); //TODO Need to set to low priority } else { THEKERNEL->streams->printf("Error: Spindle feedback pin has to be on P0 or P2.\n"); delete this; diff --git a/src/modules/tools/switch/Switch.cpp b/src/modules/tools/switch/Switch.cpp index 5e1831b4..0b27f8a6 100644 --- a/src/modules/tools/switch/Switch.cpp +++ b/src/modules/tools/switch/Switch.cpp @@ -214,6 +214,10 @@ void Switch::on_config_reload(void *argument) // SIGMADELTA THEKERNEL->slow_ticker->attach(1000, this->sigmadelta_pin, &Pwm::on_tick); } + + // for commands we need to replace _ for space + std::replace(output_on_command.begin(), output_on_command.end(), '_', ' '); // replace _ with space + std::replace(output_off_command.begin(), output_off_command.end(), '_', ' '); // replace _ with space } bool Switch::match_input_on_gcode(const Gcode *gcode) const diff --git a/src/modules/tools/temperaturecontrol/max31855.cpp b/src/modules/tools/temperaturecontrol/max31855.cpp index 7bee48b1..8688df7a 100644 --- a/src/modules/tools/temperaturecontrol/max31855.cpp +++ b/src/modules/tools/temperaturecontrol/max31855.cpp @@ -36,7 +36,7 @@ void Max31855::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) this->spi_cs_pin.from_string(THEKERNEL->config->value(module_checksum, name_checksum, chip_select_checksum)->by_default("0.16")->as_string()); this->spi_cs_pin.set(true); this->spi_cs_pin.as_output(); - + // select which SPI channel to use int spi_channel = THEKERNEL->config->value(module_checksum, name_checksum, spi_channel_checksum)->by_default(0)->as_number(); PinName miso; @@ -94,7 +94,7 @@ float Max31855::read_temp() // uint16_t data2 = spi->write(0); this->spi_cs_pin.set(true); - + float temperature; //Process temp diff --git a/src/modules/tools/temperaturecontrol/predefined_thermistors.h b/src/modules/tools/temperaturecontrol/predefined_thermistors.h index f2400d9f..410706dd 100644 --- a/src/modules/tools/temperaturecontrol/predefined_thermistors.h +++ b/src/modules/tools/temperaturecontrol/predefined_thermistors.h @@ -24,7 +24,7 @@ typedef struct { float c1, c2, c3; } const thermistor_table_t; -// Use one of the following scripts to calcuate the coefficients: +// Use one of the following scripts to calculate the coefficients: // - http://www.thinksrs.com/downloads/programs/Therm%20Calc/NTCCalibrator/NTCcalculator.htm // - https://github.com/MarlinFirmware/Marlin/blob/Development/Marlin/scripts/createTemperatureLookupMarlin.py static const thermistor_table_t predefined_thermistors[] { diff --git a/src/modules/tools/toolmanager/Tool.h b/src/modules/tools/toolmanager/Tool.h index d7a50e24..59e6fe41 100644 --- a/src/modules/tools/toolmanager/Tool.h +++ b/src/modules/tools/toolmanager/Tool.h @@ -26,3 +26,4 @@ class Tool : public Module float offset[3]; uint16_t identifier; }; + diff --git a/src/modules/tools/zprobe/DeltaCalibrationStrategy.cpp b/src/modules/tools/zprobe/DeltaCalibrationStrategy.cpp new file mode 100644 index 00000000..43806281 --- /dev/null +++ b/src/modules/tools/zprobe/DeltaCalibrationStrategy.cpp @@ -0,0 +1,424 @@ +#include "DeltaCalibrationStrategy.h" +#include "Kernel.h" +#include "Config.h" +#include "Robot.h" +#include "StreamOutputPool.h" +#include "Gcode.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "PublicDataRequest.h" +#include "EndstopsPublicAccess.h" +#include "PublicData.h" +#include "Conveyor.h" +#include "ZProbe.h" +#include "BaseSolution.h" +#include "StepperMotor.h" + +#include +#include + +#define radius_checksum CHECKSUM("radius") +#define initial_height_checksum CHECKSUM("initial_height") + +// deprecated +#define probe_radius_checksum CHECKSUM("probe_radius") + +bool DeltaCalibrationStrategy::handleConfig() +{ + // default is probably wrong + float r= THEKERNEL->config->value(leveling_strategy_checksum, delta_calibration_strategy_checksum, radius_checksum)->by_default(-1)->as_number(); + if(r == -1) { + // deprecated config syntax] + r = THEKERNEL->config->value(zprobe_checksum, probe_radius_checksum)->by_default(100.0F)->as_number(); + } + this->probe_radius= r; + + // the initial height above the bed we stop the intial move down after home to find the bed + // this should be a height that is enough that the probe will not hit the bed and is an offset from max_z (can be set to 0 if max_z takes into account the probe offset) + this->initial_height= THEKERNEL->config->value(leveling_strategy_checksum, delta_calibration_strategy_checksum, initial_height_checksum)->by_default(10)->as_number(); + return true; +} + +bool DeltaCalibrationStrategy::handleGcode(Gcode *gcode) +{ + if( gcode->has_g) { + // G code processing + if( gcode->g == 32 ) { // auto calibration for delta, Z bed mapping for cartesian + // first wait for an empty queue i.e. no moves left + THEKERNEL->conveyor->wait_for_idle(); + + // turn off any compensation transform as it will be invalidated anyway by this + THEROBOT->compensationTransform= nullptr; + + if(!gcode->has_letter('R')) { + if(!calibrate_delta_endstops(gcode)) { + gcode->stream->printf("Calibration failed to complete, check the initial probe height and/or initial_height settings\n"); + return true; + } + } + if(!gcode->has_letter('E')) { + if(!calibrate_delta_radius(gcode)) { + gcode->stream->printf("Calibration failed to complete, check the initial probe height and/or initial_height settings\n"); + return true; + } + } + gcode->stream->printf("Calibration complete, save settings with M500\n"); + return true; + + }else if (gcode->g == 29) { + // probe the 7 points + if(!probe_delta_points(gcode)) { + gcode->stream->printf("Calibration failed to complete, check the initial probe height and/or initial_height settings\n"); + } + return true; + } + + } else if(gcode->has_m) { + // handle mcodes + } + + return false; +} + +// calculate the X and Y positions for the three towers given the radius from the center +static std::tuple getCoordinates(float radius) +{ + float px = 0.866F * radius; // ~sin(60) + float py = 0.5F * radius; // cos(60) + float t1x = -px, t1y = -py; // X Tower + float t2x = px, t2y = -py; // Y Tower + float t3x = 0.0F, t3y = radius; // Z Tower + return std::make_tuple(t1x, t1y, t2x, t2y, t3x, t3y); +} + + +// Probes the 7 points on a delta can be used for off board calibration +bool DeltaCalibrationStrategy::probe_delta_points(Gcode *gcode) +{ + float bedht= findBed(); + if(isnan(bedht)) return false; + + gcode->stream->printf("initial Bed ht is %f mm\n", bedht); + + // check probe ht + float mm; + if(!zprobe->doProbeAt(mm, 0, 0)) return false; + float dz = zprobe->getProbeHeight() - mm; + gcode->stream->printf("center probe: %1.4f\n", dz); + + // get probe points + float t1x, t1y, t2x, t2y, t3x, t3y; + std::tie(t1x, t1y, t2x, t2y, t3x, t3y) = getCoordinates(this->probe_radius); + + // gather probe points + float pp[][2] {{t1x, t1y}, {t2x, t2y}, {t3x, t3y}, {0, 0}, {-t1x, -t1y}, {-t2x, -t2y}, {-t3x, -t3y}}; + + float max_delta= 0; + float last_z= NAN; + float start_z; + std::tie(std::ignore, std::ignore, start_z)= THEROBOT->get_axis_position(); + + for(auto& i : pp) { + float mm; + if(!zprobe->doProbeAt(mm, i[0], i[1])) return false; + float z = mm; + if(gcode->subcode == 0) { + gcode->stream->printf("X:%1.4f Y:%1.4f Z:%1.4f A:%1.4f B:%1.4f C:%1.4f\n", + i[0], i[1], z, + THEROBOT->actuators[0]->get_current_position()+z, + THEROBOT->actuators[1]->get_current_position()+z, + THEROBOT->actuators[2]->get_current_position()+z); + + }else if(gcode->subcode == 1) { + // format that can be pasted here http://escher3d.com/pages/wizards/wizarddelta.php + gcode->stream->printf("X%1.4f Y%1.4f Z%1.4f\n", i[0], i[1], start_z - z); + } + + if(isnan(last_z)) { + last_z= z; + }else{ + max_delta= std::max(max_delta, fabsf(z-last_z)); + } + } + + gcode->stream->printf("max delta: %f\n", max_delta); + + return true; +} + +float DeltaCalibrationStrategy::findBed() +{ + // home + zprobe->home(); + + // move to an initial position fast so as to not take all day, we move down max_z - initial_height, which is set in config, default 10mm + float deltaz= zprobe->getMaxZ() - initial_height; + zprobe->coordinated_move(NAN, NAN, -deltaz, zprobe->getFastFeedrate(), true); + + // find bed, run at slow rate so as to not hit bed hard + float mm; + if(!zprobe->run_probe(mm, false)) return NAN; + zprobe->return_probe(mm); + + // leave the probe zprobe->getProbeHeight() above bed + float dz= zprobe->getProbeHeight() - mm; + zprobe->coordinated_move(NAN, NAN, dz, zprobe->getFastFeedrate(), true); // relative move + + return mm + deltaz - zprobe->getProbeHeight(); // distance to move from home to 5mm above bed +} + +/* Run a calibration routine for a delta + 1. Home + 2. probe for z bed + 3. probe initial tower positions + 4. set initial trims such that trims will be minimal negative values + 5. home, probe three towers again + 6. calculate trim offset and apply to all trims + 7. repeat 5, 6 until it converges on a solution +*/ + +bool DeltaCalibrationStrategy::calibrate_delta_endstops(Gcode *gcode) +{ + float target = 0.03F; + if(gcode->has_letter('I')) target = gcode->get_value('I'); // override default target + if(gcode->has_letter('J')) this->probe_radius = gcode->get_value('J'); // override default probe radius + + bool keep = false; + if(gcode->has_letter('K')) keep = true; // keep current settings + + gcode->stream->printf("Calibrating Endstops: target %fmm, radius %fmm\n", target, this->probe_radius); + + // get probe points + float t1x, t1y, t2x, t2y, t3x, t3y; + std::tie(t1x, t1y, t2x, t2y, t3x, t3y) = getCoordinates(this->probe_radius); + + float trimx = 0.0F, trimy = 0.0F, trimz = 0.0F; + if(!keep) { + // zero trim values + if(!set_trim(0, 0, 0, gcode->stream)) return false; + + } else { + // get current trim, and continue from that + if (get_trim(trimx, trimy, trimz)) { + gcode->stream->printf("Current Trim X: %f, Y: %f, Z: %f\r\n", trimx, trimy, trimz); + + } else { + gcode->stream->printf("Could not get current trim, are endstops enabled?\n"); + return false; + } + } + + // find the bed, as we potentially have a temporary z probe we don't know how low under the nozzle it is + // so we need to find the initial place that the probe triggers when it hits the bed + float bedht= findBed(); + if(isnan(bedht)) return false; + gcode->stream->printf("initial Bed ht is %f mm\n", bedht); + + // check probe ht + float mm; + if(!zprobe->doProbeAt(mm, 0, 0)) return false; + float dz = zprobe->getProbeHeight() - mm; + gcode->stream->printf("center probe: %1.4f\n", dz); + if(fabsf(dz) > target) { + gcode->stream->printf("Probe was not repeatable to %f mm, (%f)\n", target, dz); + return false; + } + + // get initial probes + // probe the base of the X tower + if(!zprobe->doProbeAt(mm, t1x, t1y)) return false; + float t1z = mm; + gcode->stream->printf("T1-0 Z:%1.4f\n", t1z); + + // probe the base of the Y tower + if(!zprobe->doProbeAt(mm, t2x, t2y)) return false; + float t2z = mm; + gcode->stream->printf("T2-0 Z:%1.4f\n", t2z); + + // probe the base of the Z tower + if(!zprobe->doProbeAt(mm, t3x, t3y)) return false; + float t3z = mm; + gcode->stream->printf("T3-0 Z:%1.4f\n", t3z); + + float trimscale = 1.2522F; // empirically determined + + auto mmx = std::minmax({t1z, t2z, t3z}); + if((mmx.second - mmx.first) <= target) { + gcode->stream->printf("trim already set within required parameters: delta %f\n", mmx.second - mmx.first); + return true; + } + + // set trims to worst case so we always have a negative trim + trimx += (mmx.first - t1z) * trimscale; + trimy += (mmx.first - t2z) * trimscale; + trimz += (mmx.first - t3z) * trimscale; + + for (int i = 1; i <= 10; ++i) { + // set trim + if(!set_trim(trimx, trimy, trimz, gcode->stream)) return false; + + // home and move probe to start position just above the bed + zprobe->home(); + zprobe->coordinated_move(NAN, NAN, -bedht, zprobe->getFastFeedrate(), true); // do a relative move from home to the point above the bed + + // probe the base of the X tower + if(!zprobe->doProbeAt(mm, t1x, t1y)) return false; + t1z = mm; + gcode->stream->printf("T1-%d Z:%1.4f\n", i, t1z); + + // probe the base of the Y tower + if(!zprobe->doProbeAt(mm, t2x, t2y)) return false; + t2z = mm; + gcode->stream->printf("T2-%d Z:%1.4f\n", i, t2z); + + // probe the base of the Z tower + if(!zprobe->doProbeAt(mm, t3x, t3y)) return false; + t3z = mm; + gcode->stream->printf("T3-%d Z:%1.4f\n", i, t3z); + + mmx = std::minmax({t1z, t2z, t3z}); + if((mmx.second - mmx.first) <= target) { + gcode->stream->printf("trim set to within required parameters: delta %f\n", mmx.second - mmx.first); + break; + } + + // set new trim values based on min difference + trimx += (mmx.first - t1z) * trimscale; + trimy += (mmx.first - t2z) * trimscale; + trimz += (mmx.first - t3z) * trimscale; + + // flush the output + THEKERNEL->call_event(ON_IDLE); + } + + if((mmx.second - mmx.first) > target) { + gcode->stream->printf("WARNING: trim did not resolve to within required parameters: delta %f\n", mmx.second - mmx.first); + } + + return true; +} + +/* + probe edges to get outer positions, then probe center + modify the delta radius until center and X converge +*/ + +bool DeltaCalibrationStrategy::calibrate_delta_radius(Gcode *gcode) +{ + float target = 0.03F; + if(gcode->has_letter('I')) target = gcode->get_value('I'); // override default target + if(gcode->has_letter('J')) this->probe_radius = gcode->get_value('J'); // override default probe radius + + gcode->stream->printf("Calibrating delta radius: target %f, radius %f\n", target, this->probe_radius); + + // get probe points + float t1x, t1y, t2x, t2y, t3x, t3y; + std::tie(t1x, t1y, t2x, t2y, t3x, t3y) = getCoordinates(this->probe_radius); + + // find the bed, as we potentially have a temporary z probe we don't know how low under the nozzle it is + // so we need to find thr initial place that the probe triggers when it hits the bed + float bedht= findBed(); + if(isnan(bedht)) return false; + gcode->stream->printf("initial Bed ht is %f mm\n", bedht); + + // check probe ht + float mm; + if(!zprobe->doProbeAt(mm, 0, 0)) return false; + float dz = zprobe->getProbeHeight() - mm; + gcode->stream->printf("center probe: %1.4f\n", dz); + if(fabsf(dz) > target) { + gcode->stream->printf("Probe was not repeatable to %f mm, (%f)\n", target, dz); + return false; + } + + // probe center to get reference point at this Z height + float dc; + if(!zprobe->doProbeAt(dc, 0, 0)) return false; + gcode->stream->printf("CT Z:%1.3f\n", dc); + float cmm = dc; + + // get current delta radius + float delta_radius = 0.0F; + BaseSolution::arm_options_t options; + if(THEROBOT->arm_solution->get_optional(options)) { + delta_radius = options['R']; + } + if(delta_radius == 0.0F) { + gcode->stream->printf("This appears to not be a delta arm solution\n"); + return false; + } + options.clear(); + + bool good= false; + float drinc = 2.5F; // approx + for (int i = 1; i <= 10; ++i) { + // probe t1, t2, t3 and get average, but use coordinated moves, probing center won't change + float dx, dy, dz; + if(!zprobe->doProbeAt(dx, t1x, t1y)) return false; + gcode->stream->printf("T1-%d Z:%1.3f\n", i, dx); + if(!zprobe->doProbeAt(dy, t2x, t2y)) return false; + gcode->stream->printf("T2-%d Z:%1.3f\n", i, dy); + if(!zprobe->doProbeAt(dz, t3x, t3y)) return false; + gcode->stream->printf("T3-%d Z:%1.3f\n", i, dz); + + // now look at the difference and reduce it by adjusting delta radius + float m = (dx + dy + dz) / 3.0F; + float d = cmm - m; + gcode->stream->printf("C-%d Z-ave:%1.4f delta: %1.3f\n", i, m, d); + + if(fabsf(d) <= target){ + good= true; + break; // resolution of success + } + + // increase delta radius to adjust for low center + // decrease delta radius to adjust for high center + delta_radius += (d * drinc); + + // set the new delta radius + options['R'] = delta_radius; + THEROBOT->arm_solution->set_optional(options); + gcode->stream->printf("Setting delta radius to: %1.4f\n", delta_radius); + + zprobe->home(); + zprobe->coordinated_move(NAN, NAN, -bedht, zprobe->getFastFeedrate(), true); // needs to be a relative coordinated move + + // flush the output + THEKERNEL->call_event(ON_IDLE); + } + + if(!good) { + gcode->stream->printf("WARNING: delta radius did not resolve to within required parameters: %f\n", target); + } + + return true; +} + +bool DeltaCalibrationStrategy::set_trim(float x, float y, float z, StreamOutput *stream) +{ + float t[3] {x, y, z}; + bool ok = PublicData::set_value( endstops_checksum, trim_checksum, t); + + if (ok) { + stream->printf("set trim to X:%f Y:%f Z:%f\n", x, y, z); + } else { + stream->printf("unable to set trim, is endstops enabled?\n"); + } + + return ok; +} + +bool DeltaCalibrationStrategy::get_trim(float &x, float &y, float &z) +{ + void *returned_data; + bool ok = PublicData::get_value( endstops_checksum, trim_checksum, &returned_data ); + + if (ok) { + float *trim = static_cast(returned_data); + x = trim[0]; + y = trim[1]; + z = trim[2]; + return true; + } + return false; +} diff --git a/src/modules/tools/zprobe/DeltaCalibrationStrategy.h b/src/modules/tools/zprobe/DeltaCalibrationStrategy.h new file mode 100644 index 00000000..34c86004 --- /dev/null +++ b/src/modules/tools/zprobe/DeltaCalibrationStrategy.h @@ -0,0 +1,30 @@ +#ifndef _DELTALEVELINGSTRATEGY +#define _DELTALEVELINGSTRATEGY + +#include "LevelingStrategy.h" + +#define delta_calibration_strategy_checksum CHECKSUM("delta-calibration") + +class StreamOutput; + +class DeltaCalibrationStrategy : public LevelingStrategy +{ +public: + DeltaCalibrationStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe){}; + ~DeltaCalibrationStrategy(){}; + bool handleGcode(Gcode* gcode); + bool handleConfig(); + +private: + bool set_trim(float x, float y, float z, StreamOutput *stream); + bool get_trim(float& x, float& y, float& z); + bool calibrate_delta_endstops(Gcode *gcode); + bool calibrate_delta_radius(Gcode *gcode); + bool probe_delta_points(Gcode *gcode); + float findBed(); + + float probe_radius; + float initial_height; +}; + +#endif diff --git a/src/modules/tools/zprobe/DeltaGridStrategy.cpp b/src/modules/tools/zprobe/DeltaGridStrategy.cpp new file mode 100644 index 00000000..93875cd0 --- /dev/null +++ b/src/modules/tools/zprobe/DeltaGridStrategy.cpp @@ -0,0 +1,595 @@ +/* + This code is derived from (and mostly copied from) Johann Rocholls code at https://github.com/jcrocholl/Marlin/blob/deltabot/Marlin/Marlin_main.cpp + license is the same as his code. + + Summary + ------- + Probes grid_size points in X and Y (total probes grid_size * grid_size) and stores the relative offsets from the 0,0 Z height + When enabled every move will calculate the Z offset based on interpolating the height offset within the grids nearest 4 points. + + Configuration + ------------- + The strategy must be enabled in the config as well as zprobe. + + leveling-strategy.delta-grid.enable true + + The radius of the bed must be specified with... + + leveling-strategy.delta-grid.radius 50 + + this needs to be at least as big as the maximum printing radius as moves outside of this will not be compensated for correctly + + The size of the grid can be set with... + + leveling-strategy.delta-grid.size 7 + + this is the X and Y size of the grid, it must be an odd number, the default is 7 which is 49 probe points + + Optionally probe offsets from the nozzle or tool head can be defined with... + + leveling-strategy.delta-grid.probe_offsets 0,0,0 # probe offsetrs x,y,z + + they may also be set with M565 X0 Y0 Z0 + + If the saved grid is to be loaded on boot then this must be set in the config... + + leveling-strategy.delta-grid.save true + + Then when M500 is issued it will save M375 which will cause the grid to be loaded on boot. The default is to not autoload the grid on boot + + Optionally an initial_height can be set that tell the intial probe where to stop the fast decent before it probes, this should be around 5-10mm above the bed + leveling-strategy.delta-grid.initial_height 10 + + + Usage + ----- + G29 test probes in a spiral pattern within the radius producing a map of offsets, this can be imported into a graphing program to visualize the bed heights + optional parameters {{In}} sets the number of points to the value n, {{Jn}} sets the radius for this probe. + + G31 probes the grid and turns the compensation on, this will remain in effect until reset or M561/M370 + optional parameters {{Jn}} sets the radius for this probe, which gets saved with M375 + + M370 clears the grid and turns off compensation + M374 Save grid to /sd/delta.grid + M374.1 delete /sd/delta.grid + M375 Load the grid from /sd/delta.grid and enable compensation + M375.1 display the current grid + M561 clears the grid and turns off compensation + M565 defines the probe offsets from the nozzle or tool head + + + M500 saves the probe points + M503 displays the current settings +*/ + +#include "DeltaGridStrategy.h" + +#include "Kernel.h" +#include "Config.h" +#include "Robot.h" +#include "StreamOutputPool.h" +#include "Gcode.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "PublicDataRequest.h" +#include "PublicData.h" +#include "Conveyor.h" +#include "ZProbe.h" +#include "nuts_bolts.h" +#include "utils.h" + +#include +#include +#include +#include + +#define grid_radius_checksum CHECKSUM("radius") +#define grid_size_checksum CHECKSUM("size") +#define tolerance_checksum CHECKSUM("tolerance") +#define save_checksum CHECKSUM("save") +#define probe_offsets_checksum CHECKSUM("probe_offsets") +#define initial_height_checksum CHECKSUM("initial_height") +#define x_max_checksum CHECKSUM("x_max") +#define y_max_checksum CHECKSUM("y_max") +#define do_home_checksum CHECKSUM("do_home") +#define is_square_checksum CHECKSUM("is_square") + +#define GRIDFILE "/sd/delta.grid" + +DeltaGridStrategy::DeltaGridStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe) +{ + grid= nullptr; +} + +DeltaGridStrategy::~DeltaGridStrategy() +{ + if(grid != nullptr) free(grid); +} + +bool DeltaGridStrategy::handleConfig() +{ + grid_radius = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, grid_radius_checksum)->by_default(50.0F)->as_number(); + grid_size = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, grid_size_checksum)->by_default(7)->as_number(); + tolerance = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, tolerance_checksum)->by_default(0.03F)->as_number(); + save = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, save_checksum)->by_default(false)->as_bool(); + do_home = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, do_home_checksum)->by_default(true)->as_bool(); + is_square = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, is_square_checksum)->by_default(false)->as_bool(); + + if (is_square) + { + x_max = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, x_max_checksum)->by_default(0.0F)->as_number(); + y_max = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, y_max_checksum)->by_default(0.0F)->as_number(); + + // intelligently set defaults. + if (x_max >= 1.0F) grid_radius = x_max; + if (x_max < 1.0F) x_max = grid_radius; + if (y_max >= 1.0F) grid_radius = y_max; + if (y_max < 1.0F) y_max = grid_radius; + if (x_max >= 1.0F && y_max >= 1.0F) grid_radius = std::max(x_max, y_max); + } + else + { + x_max = grid_radius; + y_max = grid_radius; + } + + // the initial height above the bed we stop the intial move down after home to find the bed + // this should be a height that is enough that the probe will not hit the bed and is an offset from max_z (can be set to 0 if max_z takes into account the probe offset) + this->initial_height = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, initial_height_checksum)->by_default(10)->as_number(); + + // Probe offsets xxx,yyy,zzz + { + std::string po = THEKERNEL->config->value(leveling_strategy_checksum, delta_grid_leveling_strategy_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string(); + std::vector v = parse_number_list(po.c_str()); + if(v.size() >= 3) { + this->probe_offsets = std::make_tuple(v[0], v[1], v[2]); + } + } + + grid=(float *)malloc(grid_size * grid_size * sizeof(float)); + + reset_bed_level(); + + return true; +} + +void DeltaGridStrategy::save_grid(StreamOutput *stream) +{ + if(isnan(grid[0])) { + stream->printf("error:No grid to save\n"); + return; + } + + FILE *fp = fopen(GRIDFILE, "w"); + if(fp == NULL) { + stream->printf("error:Failed to open grid file %s\n", GRIDFILE); + return; + } + + if(fwrite(&grid_size, sizeof(uint8_t), 1, fp) != 1) { + stream->printf("error:Failed to write grid size\n"); + fclose(fp); + return; + } + + if(fwrite(&grid_radius, sizeof(float), 1, fp) != 1) { + stream->printf("error:Failed to write grid radius\n"); + fclose(fp); + return; + } + + for (int y = 0; y < grid_size; y++) { + for (int x = 0; x < grid_size; x++) { + if(fwrite(&grid[x + (grid_size*y)], sizeof(float), 1, fp) != 1) { + stream->printf("error:Failed to write grid\n"); + fclose(fp); + return; + } + } + } + stream->printf("grid saved to %s\n", GRIDFILE); + fclose(fp); +} + +bool DeltaGridStrategy::load_grid(StreamOutput *stream) +{ + FILE *fp = fopen(GRIDFILE, "r"); + if(fp == NULL) { + stream->printf("error:Failed to open grid %s\n", GRIDFILE); + return false; + } + + uint8_t size; + float radius; + + if(fread(&size, sizeof(uint8_t), 1, fp) != 1) { + stream->printf("error:Failed to read grid size\n"); + fclose(fp); + return false; + } + + if(size != grid_size) { + stream->printf("error:grid size is different read %d - config %d\n", size, grid_size); + fclose(fp); + return false; + } + + if(fread(&radius, sizeof(float), 1, fp) != 1) { + stream->printf("error:Failed to read grid radius\n"); + fclose(fp); + return false; + } + + if(radius != grid_radius) { + stream->printf("warning:grid radius is different read %f - config %f, overriding config\n", radius, grid_radius); + grid_radius= radius; + } + + for (int y = 0; y < grid_size; y++) { + for (int x = 0; x < grid_size; x++) { + if(fread(&grid[x + (grid_size*y)], sizeof(float), 1, fp) != 1) { + stream->printf("error:Failed to read grid\n"); + fclose(fp); + return false; + } + } + } + stream->printf("grid loaded, radius: %f, size: %d\n", grid_radius, grid_size); + fclose(fp); + return true; +} + +bool DeltaGridStrategy::probe_grid(int n, float radius, StreamOutput *stream) +{ + if(n < 5) { + stream->printf("Need at least a 5x5 grid to probe\n"); + return true; + } + + float initial_z = findBed(); + if(isnan(initial_z)) return false; + + float d= ((radius*2) / (n - 1)); + + for (int c = 0; c < n; ++c) { + float y = -radius + d*c; + for (int r = 0; r < n; ++r) { + float x = -radius + d*r; + // Avoid probing the corners (outside the round or hexagon print surface) on a delta printer. + float distance_from_center = sqrtf(x*x + y*y); + float z= 0.0F; + if ((!is_square && (distance_from_center <= radius)) || + (is_square && (x < -x_max || x > x_max || y < -y_max || y > y_max))) { + float mm; + if(!zprobe->doProbeAt(mm, x, y)) return false; + z = zprobe->getProbeHeight() - mm; + } + stream->printf("%8.4f ", z); + } + stream->printf("\n"); + } + return true; +} + +// taken from Oskars PR #713 +bool DeltaGridStrategy::probe_spiral(int n, float radius, StreamOutput *stream) +{ + float a = radius / (2 * sqrtf(n * M_PI)); + float step_length = radius * radius / (2 * a * n); + + float initial_z = findBed(); + if(isnan(initial_z)) return false; + + auto theta = [a](float length) {return sqrtf(2*length/a); }; + + float maxz= NAN, minz= NAN; + for (int i = 0; i < n; i++) { + float angle = theta(i * step_length); + float r = angle * a; + // polar to cartesian + float x = r * cosf(angle); + float y = r * sinf(angle); + + float mm; + if (!zprobe->doProbeAt(mm, x, y)) return false; + float z = zprobe->getProbeHeight() - mm; + stream->printf("PROBE: X%1.4f, Y%1.4f, Z%1.4f\n", x, y, z); + if(isnan(maxz) || z > maxz) maxz= z; + if(isnan(minz) || z < minz) minz= z; + } + + stream->printf("max: %1.4f, min: %1.4f, delta: %1.4f\n", maxz, minz, maxz-minz); + return true; +} + +bool DeltaGridStrategy::handleGcode(Gcode *gcode) +{ + if(gcode->has_g) { + if (gcode->g == 29) { // do a probe to test flatness + // first wait for an empty queue i.e. no moves left + THEKERNEL->conveyor->wait_for_idle(); + + int n= gcode->has_letter('I') ? gcode->get_value('I') : 0; + float radius = grid_radius; + if(gcode->has_letter('J')) radius = gcode->get_value('J'); // override default probe radius + if(gcode->subcode == 1){ + if(n==0) n= 50; + probe_spiral(n, radius, gcode->stream); + }else{ + if(n==0) n= 7; + probe_grid(n, radius, gcode->stream); + } + + return true; + + } else if( gcode->g == 31 ) { // do a grid probe + // first wait for an empty queue i.e. no moves left + THEKERNEL->conveyor->wait_for_idle(); + + if(!doProbe(gcode)) { + gcode->stream->printf("Probe failed to complete, check the initial probe height and/or initial_height settings\n"); + } else { + gcode->stream->printf("Probe completed\n"); + } + return true; + } + + } else if(gcode->has_m) { + if(gcode->m == 370 || gcode->m == 561) { // M370: Clear bed, M561: Set Identity Transform + // delete the compensationTransform in robot + setAdjustFunction(false); + reset_bed_level(); + gcode->stream->printf("grid cleared and disabled\n"); + return true; + + } else if(gcode->m == 374) { // M374: Save grid, M374.1: delete saved grid + if(gcode->subcode == 1) { + remove(GRIDFILE); + gcode->stream->printf("%s deleted\n", GRIDFILE); + } else { + save_grid(gcode->stream); + } + + return true; + + } else if(gcode->m == 375) { // M375: load grid, M375.1 display grid + if(gcode->subcode == 1) { + print_bed_level(gcode->stream); + } else { + if(load_grid(gcode->stream)) setAdjustFunction(true); + } + return true; + + } else if(gcode->m == 565) { // M565: Set Z probe offsets + float x = 0, y = 0, z = 0; + if(gcode->has_letter('X')) x = gcode->get_value('X'); + if(gcode->has_letter('Y')) y = gcode->get_value('Y'); + if(gcode->has_letter('Z')) z = gcode->get_value('Z'); + probe_offsets = std::make_tuple(x, y, z); + return true; + + } else if(gcode->m == 500 || gcode->m == 503) { // M500 save, M503 display + float x, y, z; + std::tie(x, y, z) = probe_offsets; + gcode->stream->printf(";Probe offsets:\nM565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z); + if(save) { + if(!isnan(grid[0])) gcode->stream->printf(";Load saved grid\nM375\n"); + else if(gcode->m == 503) gcode->stream->printf(";WARNING No grid to save\n"); + } + return true; + } + } + + return false; +} + +// These are convenience defines to keep the code as close to the original as possible it also saves memory and flash +// set the rectangle in which to probe +#define LEFT_PROBE_BED_POSITION (-grid_radius) +#define RIGHT_PROBE_BED_POSITION (grid_radius) +#define BACK_PROBE_BED_POSITION (grid_radius) +#define FRONT_PROBE_BED_POSITION (-grid_radius) + +// probe at the points of a lattice grid +#define AUTO_BED_LEVELING_GRID_X ((RIGHT_PROBE_BED_POSITION - LEFT_PROBE_BED_POSITION) / (grid_size - 1)) +#define AUTO_BED_LEVELING_GRID_Y ((BACK_PROBE_BED_POSITION - FRONT_PROBE_BED_POSITION) / (grid_size - 1)) + +#define X_PROBE_OFFSET_FROM_EXTRUDER std::get<0>(probe_offsets) +#define Y_PROBE_OFFSET_FROM_EXTRUDER std::get<1>(probe_offsets) +#define Z_PROBE_OFFSET_FROM_EXTRUDER std::get<2>(probe_offsets) + +void DeltaGridStrategy::setAdjustFunction(bool on) +{ + if(on) { + // set the compensationTransform in robot + THEROBOT->compensationTransform = [this](float target[3]) { doCompensation(target); }; + } else { + // clear it + THEROBOT->compensationTransform = nullptr; + } +} + +float DeltaGridStrategy::findBed() +{ + if (do_home) zprobe->home(); + // move to an initial position fast so as to not take all day, we move down max_z - initial_height, which is set in config, default 10mm + float deltaz = initial_height; + zprobe->coordinated_move(NAN, NAN, deltaz, zprobe->getFastFeedrate()); + zprobe->coordinated_move(0, 0, NAN, zprobe->getFastFeedrate()); // move to 0,0 + + // find bed at 0,0 run at slow rate so as to not hit bed hard + float mm; + if(!zprobe->run_probe(mm, false)) return NAN; + + // leave the probe zprobe->getProbeHeight() above bed + zprobe->return_probe(mm); + + float dz= zprobe->getProbeHeight() - mm; + zprobe->coordinated_move(NAN, NAN, dz, zprobe->getFastFeedrate(), true); // relative move + + return mm + deltaz - zprobe->getProbeHeight(); // distance to move from home to 5mm above bed +} + +bool DeltaGridStrategy::doProbe(Gcode *gc) +{ + gc->stream->printf("Delta Grid Probe...\n"); + setAdjustFunction(false); + reset_bed_level(); + + if(gc->has_letter('J')) grid_radius = gc->get_value('J'); // override default probe radius, will get saved + + float radius = grid_radius; + // find bed, and leave probe probe height above bed + float initial_z = findBed(); + if(isnan(initial_z)) { + gc->stream->printf("Finding bed failed, check the maxz and initial height settings\n"); + return false; + } + + gc->stream->printf("Probe start ht is %f mm, probe radius is %f mm, grid size is %dx%d\n", initial_z, radius, grid_size, grid_size); + + // do first probe for 0,0 + float mm; + if(!zprobe->doProbeAt(mm, -X_PROBE_OFFSET_FROM_EXTRUDER, -Y_PROBE_OFFSET_FROM_EXTRUDER)) return false; + float z_reference = zprobe->getProbeHeight() - mm; // this should be zero + gc->stream->printf("probe at 0,0 is %f mm\n", z_reference); + + // probe all the points in the grid within the given radius + for (int yCount = 0; yCount < grid_size; yCount++) { + float yProbe = FRONT_PROBE_BED_POSITION + AUTO_BED_LEVELING_GRID_Y * yCount; + int xStart, xStop, xInc; + if (yCount % 2) { + xStart = 0; + xStop = grid_size; + xInc = 1; + } else { + xStart = grid_size - 1; + xStop = -1; + xInc = -1; + } + + for (int xCount = xStart; xCount != xStop; xCount += xInc) { + float xProbe = LEFT_PROBE_BED_POSITION + AUTO_BED_LEVELING_GRID_X * xCount; + + // avoid probing outside of x min/max on a cartesian + if (is_square) + { + if (xProbe < -x_max || xProbe > x_max || yProbe < -y_max || yProbe > y_max) continue; + } + else + { + // Avoid probing the corners (outside the round or hexagon print surface) on a delta printer. + float distance_from_center = sqrtf(xProbe * xProbe + yProbe * yProbe); + if (distance_from_center > radius) continue; + } + + if(!zprobe->doProbeAt(mm, xProbe - X_PROBE_OFFSET_FROM_EXTRUDER, yProbe - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false; + float measured_z = zprobe->getProbeHeight() - mm - z_reference; // this is the delta z from bed at 0,0 + gc->stream->printf("DEBUG: X%1.4f, Y%1.4f, Z%1.4f\n", xProbe, yProbe, measured_z); + grid[xCount + (grid_size * yCount)] = measured_z; + } + } + + extrapolate_unprobed_bed_level(); + print_bed_level(gc->stream); + + setAdjustFunction(true); + + return true; +} + +void DeltaGridStrategy::extrapolate_one_point(int x, int y, int xdir, int ydir) +{ + if (!isnan(grid[x + (grid_size*y)])) { + return; // Don't overwrite good values. + } + float a = 2 * grid[(x + xdir) + (y*grid_size)] - grid[(x + xdir * 2) + (y*grid_size)]; // Left to right. + float b = 2 * grid[x + ((y + ydir) * grid_size)] - grid[x + ((y + ydir * 2) * grid_size)]; // Front to back. + float c = 2 * grid[(x + xdir) + ((y + ydir) * grid_size)] - grid[(x + xdir * 2) + ((y + ydir * 2) * grid_size)]; // Diagonal. + float median = c; // Median is robust (ignores outliers). + if (a < b) { + if (b < c) median = b; + if (c < a) median = a; + } else { // b <= a + if (c < b) median = b; + if (a < c) median = a; + } + grid[x + (grid_size*y)] = median; +} + +// Fill in the unprobed points (corners of circular print surface) +// using linear extrapolation, away from the center. +void DeltaGridStrategy::extrapolate_unprobed_bed_level() +{ + int half = (grid_size - 1) / 2; + for (int y = 0; y <= half; y++) { + for (int x = 0; x <= half; x++) { + if (x + y < 3) continue; + extrapolate_one_point(half - x, half - y, x > 1 ? +1 : 0, y > 1 ? +1 : 0); + extrapolate_one_point(half + x, half - y, x > 1 ? -1 : 0, y > 1 ? +1 : 0); + extrapolate_one_point(half - x, half + y, x > 1 ? +1 : 0, y > 1 ? -1 : 0); + extrapolate_one_point(half + x, half + y, x > 1 ? -1 : 0, y > 1 ? -1 : 0); + } + } +} + +void DeltaGridStrategy::doCompensation(float target[3]) +{ + // Adjust print surface height by linear interpolation over the bed_level array. + int half = (grid_size - 1) / 2; + float grid_x = std::max(0.001F - half, std::min(half - 0.001F, target[X_AXIS] / AUTO_BED_LEVELING_GRID_X)); + float grid_y = std::max(0.001F - half, std::min(half - 0.001F, target[Y_AXIS] / AUTO_BED_LEVELING_GRID_Y)); + int floor_x = floorf(grid_x); + int floor_y = floorf(grid_y); + float ratio_x = grid_x - floor_x; + float ratio_y = grid_y - floor_y; + float z1 = grid[(floor_x + half) + ((floor_y + half) * grid_size)]; + float z2 = grid[(floor_x + half) + ((floor_y + half + 1) * grid_size)]; + float z3 = grid[(floor_x + half + 1) + ((floor_y + half) * grid_size)]; + float z4 = grid[(floor_x + half + 1) + ((floor_y + half + 1) * grid_size)]; + float left = (1 - ratio_y) * z1 + ratio_y * z2; + float right = (1 - ratio_y) * z3 + ratio_y * z4; + float offset = (1 - ratio_x) * left + ratio_x * right; + + target[Z_AXIS] += offset; + + + /* + THEKERNEL->streams->printf("//DEBUG: TARGET: %f, %f, %f\n", target[0], target[1], target[2]); + THEKERNEL->streams->printf("//DEBUG: grid_x= %f\n", grid_x); + THEKERNEL->streams->printf("//DEBUG: grid_y= %f\n", grid_y); + THEKERNEL->streams->printf("//DEBUG: floor_x= %d\n", floor_x); + THEKERNEL->streams->printf("//DEBUG: floor_y= %d\n", floor_y); + THEKERNEL->streams->printf("//DEBUG: ratio_x= %f\n", ratio_x); + THEKERNEL->streams->printf("//DEBUG: ratio_y= %f\n", ratio_y); + THEKERNEL->streams->printf("//DEBUG: z1= %f\n", z1); + THEKERNEL->streams->printf("//DEBUG: z2= %f\n", z2); + THEKERNEL->streams->printf("//DEBUG: z3= %f\n", z3); + THEKERNEL->streams->printf("//DEBUG: z4= %f\n", z4); + THEKERNEL->streams->printf("//DEBUG: left= %f\n", left); + THEKERNEL->streams->printf("//DEBUG: right= %f\n", right); + THEKERNEL->streams->printf("//DEBUG: offset= %f\n", offset); + */ +} + + +// Print calibration results for plotting or manual frame adjustment. +void DeltaGridStrategy::print_bed_level(StreamOutput *stream) +{ + for (int y = 0; y < grid_size; y++) { + for (int x = 0; x < grid_size; x++) { + stream->printf("%7.4f ", grid[x + (grid_size*y)]); + } + stream->printf("\n"); + } +} + +// Reset calibration results to zero. +void DeltaGridStrategy::reset_bed_level() +{ + for (int y = 0; y < grid_size; y++) { + for (int x = 0; x < grid_size; x++) { + grid[x + (grid_size*y)] = NAN; + } + } +} diff --git a/src/modules/tools/zprobe/DeltaGridStrategy.h b/src/modules/tools/zprobe/DeltaGridStrategy.h new file mode 100644 index 00000000..bfa9d579 --- /dev/null +++ b/src/modules/tools/zprobe/DeltaGridStrategy.h @@ -0,0 +1,50 @@ +#pragma once + +#include "LevelingStrategy.h" + +#include +#include + +#define delta_grid_leveling_strategy_checksum CHECKSUM("delta-grid") + +class StreamOutput; +class Gcode; + +class DeltaGridStrategy : public LevelingStrategy +{ +public: + DeltaGridStrategy(ZProbe *zprobe); + ~DeltaGridStrategy(); + bool handleGcode(Gcode* gcode); + bool handleConfig(); + +private: + + void extrapolate_one_point(int x, int y, int xdir, int ydir); + void extrapolate_unprobed_bed_level(); + bool doProbe(Gcode *gc); + float findBed(); + void setAdjustFunction(bool on); + void print_bed_level(StreamOutput *stream); + void doCompensation(float target[3]); + void reset_bed_level(); + void save_grid(StreamOutput *stream); + bool load_grid(StreamOutput *stream); + bool probe_spiral(int n, float radius, StreamOutput *stream); + bool probe_grid(int n, float radius, StreamOutput *stream); + + float initial_height; + float tolerance; + + float *grid; + float grid_radius; + std::tuple probe_offsets; + uint8_t grid_size; + float x_max,y_max; + + struct { + bool save:1; + bool do_home:1; + bool is_square:1; + }; +}; diff --git a/src/modules/tools/zprobe/LevelingStrategy.h b/src/modules/tools/zprobe/LevelingStrategy.h new file mode 100644 index 00000000..6460611f --- /dev/null +++ b/src/modules/tools/zprobe/LevelingStrategy.h @@ -0,0 +1,24 @@ +/* + * A strategy called from ZProbe to handle a strategy for leveling + * examples are delta calibration, three point bed leveling, z height map + */ + +#ifndef _LEVELINGSTRATEGY +#define _LEVELINGSTRATEGY + +class ZProbe; +class Gcode; + +class LevelingStrategy +{ +public: + LevelingStrategy(ZProbe* zprobe) : zprobe(zprobe){}; + virtual ~LevelingStrategy(){}; + virtual bool handleGcode(Gcode* gcode)= 0; + virtual bool handleConfig()= 0; + +protected: + ZProbe *zprobe; + +}; +#endif diff --git a/src/modules/tools/zprobe/Plane3D.cpp b/src/modules/tools/zprobe/Plane3D.cpp new file mode 100644 index 00000000..38ad0138 --- /dev/null +++ b/src/modules/tools/zprobe/Plane3D.cpp @@ -0,0 +1,47 @@ +#include "Plane3D.h" + +Plane3D::Plane3D(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) +{ + // get the normal of the plane + Vector3 ab = v1.sub(v2); + Vector3 ac = v1.sub(v3); + + Vector3 cp = ab.cross(ac); + normal = cp.unit(); + + // ax+by+cz+d=0 + // solve for d + d = -normal.dot(v1); +} + +typedef union { float f; uint32_t u; } conv_t; +// ctor used to restore a saved plane +Plane3D::Plane3D(uint32_t a, uint32_t b, uint32_t c, uint32_t d) +{ + conv_t ca, cb, cc, cd; + ca.u= a; cb.u= b; cc.u= c; cd.u= d; + this->normal = Vector3(ca.f, cb.f, cc.f); + this->d= cd.f; +} + +void Plane3D::encode(uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) +{ + conv_t ca, cb, cc, cd; + ca.f= this->normal[0]; + cb.f= this->normal[1]; + cc.f= this->normal[2]; + cd.f= this->d; + a= ca.u; b= cb.u; c= cc.u; d= cd.u; +} + +// solve for z given x and y +// z= (-ax - by - d)/c +float Plane3D::getz(float x, float y) +{ + return ((-normal[0] * x) - (normal[1] * y) - d) / normal[2]; +} + +Vector3 Plane3D::getNormal() const +{ + return normal; +} diff --git a/src/modules/tools/zprobe/Plane3D.h b/src/modules/tools/zprobe/Plane3D.h new file mode 100644 index 00000000..2dbc0099 --- /dev/null +++ b/src/modules/tools/zprobe/Plane3D.h @@ -0,0 +1,23 @@ +#ifndef __PLANE3D_H +#define __PLANE3D_H + +#include "Vector3.h" + +#include + +// define a plane given three points +class Plane3D +{ +private: + Vector3 normal; + float d; + +public: + Plane3D(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3); + Plane3D(uint32_t a, uint32_t b, uint32_t c, uint32_t d); + float getz(float x, float y); + Vector3 getNormal() const; + void encode(uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d); +}; + +#endif diff --git a/src/modules/tools/zprobe/ThreePointStrategy.cpp b/src/modules/tools/zprobe/ThreePointStrategy.cpp new file mode 100644 index 00000000..9b9d2366 --- /dev/null +++ b/src/modules/tools/zprobe/ThreePointStrategy.cpp @@ -0,0 +1,399 @@ +/* + Author: Jim Morris (wolfmanjm@gmail.com) + License: GPL3 or better see + + Summary + ------- + Probes three user specified points on the bed and determines the plane of the bed relative to the probe. + as the head moves in X and Y it will adjust Z to keep the head tram with the bed. + + Configuration + ------------- + The strategy must be enabled in the cofnig as well as zprobe. + + leveling-strategy.three-point-leveling.enable true + + Three probe points must be defined, these are best if they are the three points of an equilateral triangle, as far apart as possible. + They can be defined in the config file as:- + + leveling-strategy.three-point-leveling.point1 100.0,0.0 # the first probe point (x,y) + leveling-strategy.three-point-leveling.point2 200.0,200.0 # the second probe point (x,y) + leveling-strategy.three-point-leveling.point3 0.0,200.0 # the third probe point (x,y) + + or they may be defined (and saved with M500) using M557 P0 X30 Y40.5 where P is 0,1,2 + + probe offsets from the nozzle or tool head can be defined with + + leveling-strategy.three-point-leveling.probe_offsets 0,0,0 # probe offsetrs x,y,z + + they may also be set with M565 X0 Y0 Z0 + + To force homing in X and Y before G32 does the probe the following can be set in config, this is the default + + leveling-strategy.three-point-leveling.home_first true # disable by setting to false + + The probe tolerance can be set using the config line + + leveling-strategy.three-point-leveling.tolerance 0.03 # the probe tolerance in mm, default is 0.03mm + + + Usage + ----- + G29 probes the three probe points and reports the Z at each point, if a plane is active it will be used to level the probe. + G32 probes the three probe points and defines the bed plane, this will remain in effect until reset or M561 + G31 reports the status + + M557 defines the probe points + M561 clears the plane and the bed leveling is disabled until G32 is run again + M565 defines the probe offsets from the nozzle or tool head + + M500 saves the probe points and the probe offsets + M503 displays the current settings +*/ + +#include "ThreePointStrategy.h" +#include "Kernel.h" +#include "Config.h" +#include "Robot.h" +#include "StreamOutputPool.h" +#include "Gcode.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "PublicDataRequest.h" +#include "PublicData.h" +#include "Conveyor.h" +#include "ZProbe.h" +#include "Plane3D.h" +#include "nuts_bolts.h" + +#include +#include +#include +#include + +#define probe_point_1_checksum CHECKSUM("point1") +#define probe_point_2_checksum CHECKSUM("point2") +#define probe_point_3_checksum CHECKSUM("point3") +#define probe_offsets_checksum CHECKSUM("probe_offsets") +#define home_checksum CHECKSUM("home_first") +#define tolerance_checksum CHECKSUM("tolerance") +#define save_plane_checksum CHECKSUM("save_plane") + +ThreePointStrategy::ThreePointStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe) +{ + for (int i = 0; i < 3; ++i) { + probe_points[i] = std::make_tuple(NAN, NAN); + } + plane = nullptr; +} + +ThreePointStrategy::~ThreePointStrategy() +{ + delete plane; +} + +bool ThreePointStrategy::handleConfig() +{ + // format is xxx,yyy for the probe points + std::string p1 = THEKERNEL->config->value(leveling_strategy_checksum, three_point_leveling_strategy_checksum, probe_point_1_checksum)->by_default("")->as_string(); + std::string p2 = THEKERNEL->config->value(leveling_strategy_checksum, three_point_leveling_strategy_checksum, probe_point_2_checksum)->by_default("")->as_string(); + std::string p3 = THEKERNEL->config->value(leveling_strategy_checksum, three_point_leveling_strategy_checksum, probe_point_3_checksum)->by_default("")->as_string(); + if(!p1.empty()) probe_points[0] = parseXY(p1.c_str()); + if(!p2.empty()) probe_points[1] = parseXY(p2.c_str()); + if(!p3.empty()) probe_points[2] = parseXY(p3.c_str()); + + // Probe offsets xxx,yyy,zzz + std::string po = THEKERNEL->config->value(leveling_strategy_checksum, three_point_leveling_strategy_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string(); + this->probe_offsets= parseXYZ(po.c_str()); + + this->home= THEKERNEL->config->value(leveling_strategy_checksum, three_point_leveling_strategy_checksum, home_checksum)->by_default(true)->as_bool(); + this->tolerance= THEKERNEL->config->value(leveling_strategy_checksum, three_point_leveling_strategy_checksum, tolerance_checksum)->by_default(0.03F)->as_number(); + this->save= THEKERNEL->config->value(leveling_strategy_checksum, three_point_leveling_strategy_checksum, save_plane_checksum)->by_default(false)->as_bool(); + return true; +} + +bool ThreePointStrategy::handleGcode(Gcode *gcode) +{ + if(gcode->has_g) { + // G code processing + if(gcode->g == 29) { // test probe points for level + if(!test_probe_points(gcode)) { + gcode->stream->printf("Probe failed to complete, probe not triggered or other error\n"); + } + return true; + + } else if( gcode->g == 31 ) { // report status + if(this->plane == nullptr) { + gcode->stream->printf("Bed leveling plane is not set\n"); + }else{ + gcode->stream->printf("Bed leveling plane normal= %f, %f, %f\n", plane->getNormal()[0], plane->getNormal()[1], plane->getNormal()[2]); + } + gcode->stream->printf("Probe is %s\n", zprobe->getProbeStatus() ? "Triggered" : "Not triggered"); + return true; + + } else if( gcode->g == 32 ) { // three point probe + // first wait for an empty queue i.e. no moves left + THEKERNEL->conveyor->wait_for_idle(); + + // clear any existing plane and compensation + delete this->plane; + this->plane= nullptr; + setAdjustFunction(false); + + if(!doProbing(gcode->stream)) { + gcode->stream->printf("Probe failed to complete, probe not triggered or other error\n"); + } else { + gcode->stream->printf("Probe completed, bed plane defined\n"); + } + return true; + } + + } else if(gcode->has_m) { + if(gcode->m == 557) { // M557 - set probe points eg M557 P0 X30 Y40.5 where P is 0,1,2 + int idx = 0; + float x = NAN, y = NAN; + if(gcode->has_letter('P')) idx = gcode->get_value('P'); + if(gcode->has_letter('X')) x = gcode->get_value('X'); + if(gcode->has_letter('Y')) y = gcode->get_value('Y'); + if(idx >= 0 && idx <= 2) { + probe_points[idx] = std::make_tuple(x, y); + }else{ + gcode->stream->printf("only 3 probe points allowed P0-P2\n"); + } + return true; + + } else if(gcode->m == 561) { // M561: Set Identity Transform with no parameters, set the saved plane if A B C D are given + delete this->plane; + if(gcode->get_num_args() == 0) { + this->plane= nullptr; + // delete the compensationTransform in robot + setAdjustFunction(false); + gcode->stream->printf("saved plane cleared\n"); + }else{ + // smoothie specific way to restore a saved plane + uint32_t a,b,c,d; + a=b=c=d= 0; + if(gcode->has_letter('A')) a = gcode->get_uint('A'); + if(gcode->has_letter('B')) b = gcode->get_uint('B'); + if(gcode->has_letter('C')) c = gcode->get_uint('C'); + if(gcode->has_letter('D')) d = gcode->get_uint('D'); + this->plane= new Plane3D(a, b, c, d); + setAdjustFunction(true); + } + return true; + + } else if(gcode->m == 565) { // M565: Set Z probe offsets + float x= 0, y= 0, z= 0; + if(gcode->has_letter('X')) x = gcode->get_value('X'); + if(gcode->has_letter('Y')) y = gcode->get_value('Y'); + if(gcode->has_letter('Z')) z = gcode->get_value('Z'); + probe_offsets = std::make_tuple(x, y, z); + return true; + + } else if(gcode->m == 500 || gcode->m == 503) { // M500 save, M503 display + float x, y, z; + gcode->stream->printf(";Probe points:\n"); + for (int i = 0; i < 3; ++i) { + std::tie(x, y) = probe_points[i]; + gcode->stream->printf("M557 P%d X%1.5f Y%1.5f\n", i, x, y); + } + gcode->stream->printf(";Probe offsets:\n"); + std::tie(x, y, z) = probe_offsets; + gcode->stream->printf("M565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z); + + // encode plane and save if set and M500 and enabled + if(this->save && this->plane != nullptr) { + if(gcode->m == 500) { + uint32_t a, b, c, d; + this->plane->encode(a, b, c, d); + gcode->stream->printf(";Saved bed plane:\nM561 A%lu B%lu C%lu D%lu \n", a, b, c, d); + }else{ + gcode->stream->printf(";The bed plane will be saved on M500\n"); + } + } + return true; + + } + #if 0 + else if(gcode->m == 9999) { + // DEBUG run a test M9999 A B C X Y set Z to A B C and test for point at X Y + Vector3 v[3]; + float x, y, z, a= 0, b= 0, c= 0; + if(gcode->has_letter('A')) a = gcode->get_value('A'); + if(gcode->has_letter('B')) b = gcode->get_value('B'); + if(gcode->has_letter('C')) c = gcode->get_value('C'); + std::tie(x, y) = probe_points[0]; v[0].set(x, y, a); + std::tie(x, y) = probe_points[1]; v[1].set(x, y, b); + std::tie(x, y) = probe_points[2]; v[2].set(x, y, c); + delete this->plane; + this->plane = new Plane3D(v[0], v[1], v[2]); + gcode->stream->printf("plane normal= %f, %f, %f\n", plane->getNormal()[0], plane->getNormal()[1], plane->getNormal()[2]); + x= 0; y=0; + if(gcode->has_letter('X')) x = gcode->get_value('X'); + if(gcode->has_letter('Y')) y = gcode->get_value('Y'); + z= getZOffset(x, y); + gcode->stream->printf("z= %f\n", z); + // tell robot to adjust z on each move + setAdjustFunction(true); + return true; + } + #endif + } + + return false; +} + +void ThreePointStrategy::homeXY() +{ + Gcode gc("G28 X0 Y0", &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc); +} + +bool ThreePointStrategy::doProbing(StreamOutput *stream) +{ + float x, y; + // check the probe points have been defined + for (int i = 0; i < 3; ++i) { + std::tie(x, y) = probe_points[i]; + if(isnan(x) || isnan(y)) { + stream->printf("Probe point P%d has not been defined, use M557 P%d Xnnn Ynnn to define it\n", i, i); + return false; + } + } + + // optionally home XY axis first, but allow for manual homing + if(this->home) + homeXY(); + + // move to the first probe point + std::tie(x, y) = probe_points[0]; + // offset by the probe XY offset + x -= std::get(this->probe_offsets); + y -= std::get(this->probe_offsets); + zprobe->coordinated_move(x, y, NAN, zprobe->getFastFeedrate()); + + // for now we use probe to find bed and not the Z min endstop + // the first probe point becomes Z == 0 effectively so if we home Z or manually set z after this, it needs to be at the first probe point + + // TODO this needs to be configurable to use min z or probe + + // find bed via probe + float mm; + if(!zprobe->run_probe(mm)) return false; + + // TODO if using probe then we probably need to set Z to 0 at first probe point, but take into account probe offset from head + THEROBOT->reset_axis_position(std::get(this->probe_offsets), Z_AXIS); + + // move up to specified probe start position + zprobe->coordinated_move(NAN, NAN, zprobe->getProbeHeight(), zprobe->getSlowFeedrate()); // move to probe start position + + // probe the three points + Vector3 v[3]; + for (int i = 0; i < 3; ++i) { + std::tie(x, y) = probe_points[i]; + // offset moves by the probe XY offset + float z = zprobe->probeDistance(x-std::get(this->probe_offsets), y-std::get(this->probe_offsets)); + if(isnan(z)) return false; // probe failed + z= zprobe->getProbeHeight() - z; // relative distance between the probe points, lower is negative z + stream->printf("DEBUG: P%d:%1.4f\n", i, z); + v[i] = Vector3(x, y, z); + } + + // if first point is not within tolerance report it, it should ideally be 0 + if(fabsf(v[0][2]) > this->tolerance) { + stream->printf("WARNING: probe is not within tolerance: %f > %f\n", fabsf(v[0][2]), this->tolerance); + } + + // define the plane + delete this->plane; + // check tolerance level here default 0.03mm + auto mmx = std::minmax({v[0][2], v[1][2], v[2][2]}); + if((mmx.second - mmx.first) <= this->tolerance) { + this->plane= nullptr; // plane is flat no need to do anything + stream->printf("DEBUG: flat plane\n"); + // clear the compensationTransform in robot + setAdjustFunction(false); + + }else{ + this->plane = new Plane3D(v[0], v[1], v[2]); + stream->printf("DEBUG: plane normal= %f, %f, %f\n", plane->getNormal()[0], plane->getNormal()[1], plane->getNormal()[2]); + setAdjustFunction(true); + } + + return true; +} + +// Probes the 3 points and reports heights +bool ThreePointStrategy::test_probe_points(Gcode *gcode) +{ + // check the probe points have been defined + float max_delta= 0; + float last_z= NAN; + for (int i = 0; i < 3; ++i) { + float x, y; + std::tie(x, y) = probe_points[i]; + if(isnan(x) || isnan(y)) { + gcode->stream->printf("Probe point P%d has not been defined, use M557 P%d Xnnn Ynnn to define it\n", i, i); + return false; + } + + float z = zprobe->probeDistance(x-std::get(this->probe_offsets), y-std::get(this->probe_offsets)); + if(isnan(z)) return false; // probe failed + gcode->stream->printf("X:%1.4f Y:%1.4f Z:%1.4f\n", x, y, z); + + if(isnan(last_z)) { + last_z= z; + }else{ + max_delta= std::max(max_delta, fabsf(z-last_z)); + } + } + + gcode->stream->printf("max delta: %f\n", max_delta); + + return true; +} + +void ThreePointStrategy::setAdjustFunction(bool on) +{ + if(on) { + // set the compensationTransform in robot + THEROBOT->compensationTransform= [this](float target[3]) { target[2] += this->plane->getz(target[0], target[1]); }; + }else{ + // clear it + THEROBOT->compensationTransform= nullptr; + } +} + +// find the Z offset for the point on the plane at x, y +float ThreePointStrategy::getZOffset(float x, float y) +{ + if(this->plane == nullptr) return NAN; + return this->plane->getz(x, y); +} + +// parse a "X,Y" string return x,y +std::tuple ThreePointStrategy::parseXY(const char *str) +{ + float x = NAN, y = NAN; + char *p; + x = strtof(str, &p); + if(p + 1 < str + strlen(str)) { + y = strtof(p + 1, nullptr); + } + return std::make_tuple(x, y); +} + +// parse a "X,Y,Z" string return x,y,z tuple +std::tuple ThreePointStrategy::parseXYZ(const char *str) +{ + float x = 0, y = 0, z= 0; + char *p; + x = strtof(str, &p); + if(p + 1 < str + strlen(str)) { + y = strtof(p + 1, &p); + if(p + 1 < str + strlen(str)) { + z = strtof(p + 1, nullptr); + } + } + return std::make_tuple(x, y, z); +} diff --git a/src/modules/tools/zprobe/ThreePointStrategy.h b/src/modules/tools/zprobe/ThreePointStrategy.h new file mode 100644 index 00000000..652f4c20 --- /dev/null +++ b/src/modules/tools/zprobe/ThreePointStrategy.h @@ -0,0 +1,41 @@ +#ifndef _THREEPOINTSTRATEGY +#define _THREEPOINTSTRATEGY + +#include "LevelingStrategy.h" + +#include +#include + +#define three_point_leveling_strategy_checksum CHECKSUM("three-point-leveling") + +class StreamOutput; +class Plane3D; + +class ThreePointStrategy : public LevelingStrategy +{ +public: + ThreePointStrategy(ZProbe *zprobe); + ~ThreePointStrategy(); + bool handleGcode(Gcode* gcode); + bool handleConfig(); + float getZOffset(float x, float y); + +private: + void homeXY(); + bool doProbing(StreamOutput *stream); + std::tuple parseXY(const char *str); + std::tuple parseXYZ(const char *str); + void setAdjustFunction(bool); + bool test_probe_points(Gcode *gcode); + + std::tuple probe_offsets; + std::tuple probe_points[3]; + Plane3D *plane; + struct { + bool home:1; + bool save:1; + }; + float tolerance; +}; + +#endif diff --git a/src/modules/tools/zprobe/ZGridStrategy.cpp b/src/modules/tools/zprobe/ZGridStrategy.cpp new file mode 100644 index 00000000..97d4d6ac --- /dev/null +++ b/src/modules/tools/zprobe/ZGridStrategy.cpp @@ -0,0 +1,711 @@ +/* + Author: Quentin Harley (quentin.harley@gmail.com) + License: GPL3 or better see + + Summary + ------- + Probes user defined amount of calculated points on the bed and creates compensation grid data of the bed surface. + Bilinear + As the head moves in X and Y it will adjust Z to keep the head level with the bed. + + Configuration + ------------- + The strategy must be enabled in the config as well as zprobe. + + leveling-strategy.ZGrid-leveling.enable true + + The bed size limits must be defined, in order for the module to calculate the calibration points + + leveling-strategy.ZGrid-leveling.bed_x 200 + leveling-strategy.ZGrid-leveling.bed_y 200 + + Machine height, used to determine probe attachment point (bed_z / 2) + + leveling-strategy.ZGrid-leveling.bed_z 20 + + Probe attachement point, if defined, overrides the calculated point + leveling-strategy.ZGrid-leveling.probe_x 0 + leveling-strategy.ZGrid-leveling.probe_y 0 + leveling-strategy.ZGrid-leveling.probe_z 30 + + + Configure for Machines with bed 0:0 at center of platform + leveling-strategy.ZGrid-leveling.bed_zero false + + configure for Machines with circular beds + leveling-strategy.ZGrid-leveling.bed_circular false + + + The number of divisions for X and Y should be defined + + leveling-strategy.ZGrid-leveling.rows 7 # X divisions (Default 5) + leveling-strategy.ZGrid-leveling.cols 9 # Y divisions (Default 5) + + + The probe offset should be defined, default to zero offset + + leveling-strategy.ZGrid-leveling.probe_offsets 0,0,16.3 + + The machine can be told to wait for probe attachment and confirmation + + leveling-strategy.ZGrid-leveling.wait_for_probe true + + The machine can be told to home in one of the following modes: + + leveling-strategy.ZGrid-leveling.home_before_probe homexyz; # nohome homexy homexyz (default) + + + Slow feedrate can be defined for probe positioning speed. Note this is not Probing slow rate - it can be set to a fast speed if required. + + leveling-strategy.ZGrid-leveling.slow_feedrate 100 # ZGrid probe positioning feedrate + + + + Usage + ----- + G32 : probes the probe points and defines the bed ZGrid, this will remain in effect until reset or M370 + G31 : reports the status - Display probe data points + + M370 : clears the ZGrid and the bed levelling is disabled until G32 is run again + M370 X7 Y9 : allocates a new grid size of 7x9 and clears as above + + M371 : moves the head to the next calibration position without saving for manual calibration + M372 : move the head to the next calibration position after saving the current probe point to memory - manual calbration + M373 : completes calibration and enables the Z compensation grid + + M374 : Save the grid to "Zgrid" on SD card + M374 S### : Save custom grid to "Zgrid.###" on SD card + + M375 : Loads grid file "Zgrid" from SD + M375 S### : Load custom grid file "Zgrid.###" + + M565 X### Y### Z### : Set new probe offsets + + M500 : saves the probe offsets + M503 : displays the current settings +*/ + +#include "ZGridStrategy.h" +#include "Kernel.h" +#include "Config.h" +#include "Robot.h" +#include "StreamOutputPool.h" +#include "Gcode.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "PublicDataRequest.h" +#include "PublicData.h" +#include "EndstopsPublicAccess.h" +#include "Conveyor.h" +#include "ZProbe.h" +#include "libs/FileStream.h" +#include "nuts_bolts.h" +//#include "platform_memory.h" +//#include "MemoryPool.h" +#include "libs/utils.h" + +#include +#include +#include +#include +#include + +#define bed_x_checksum CHECKSUM("bed_x") +#define bed_y_checksum CHECKSUM("bed_y") +#define bed_z_checksum CHECKSUM("bed_z") + +#define probe_x_checksum CHECKSUM("probe_x") +#define probe_y_checksum CHECKSUM("probe_y") +#define probe_z_checksum CHECKSUM("probe_z") + +#define slow_feedrate_checksum CHECKSUM("slow_feedrate") +#define probe_offsets_checksum CHECKSUM("probe_offsets") +#define wait_for_probe_checksum CHECKSUM("wait_for_probe") +#define home_before_probe_checksum CHECKSUM("home_before_probe") +#define center_zero_checksum CHECKSUM("center_zero") +#define circular_bed_checksum CHECKSUM("circular_bed") +#define cal_offset_x_checksum CHECKSUM("cal_offset_x") +#define cal_offset_y_checksum CHECKSUM("cal_offset_y") + +#define NOHOME 0 +#define HOMEXY 1 +#define HOMEXYZ 2 + +#define cols_checksum CHECKSUM("cols") +#define rows_checksum CHECKSUM("rows") + +#define probe_points (this->numRows * this->numCols) + + + +ZGridStrategy::ZGridStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe) +{ + this->cal[X_AXIS] = 0.0f; + this->cal[Y_AXIS] = 0.0f; + this->cal[Z_AXIS] = 30.0f; + + this->in_cal = false; + this->pData = nullptr; +} + +ZGridStrategy::~ZGridStrategy() +{ + // Free program memory for the pData grid + if(this->pData != nullptr) free(this->pData); +} + +bool ZGridStrategy::handleConfig() +{ + this->bed_x = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, bed_x_checksum)->by_default(200.0F)->as_number(); + this->bed_y = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, bed_y_checksum)->by_default(200.0F)->as_number(); + this->bed_z = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, bed_z_checksum)->by_default(20.0F)->as_number(); + + this->probe_x = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, probe_x_checksum)->by_default(this->center_zero ? this->bed_x / 2.0F : 0.0F)->as_number(); + this->probe_y = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, probe_y_checksum)->by_default(this->center_zero ? this->bed_y / 2.0F : 0.0F)->as_number(); + this->probe_z = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, probe_z_checksum)->by_default(this->bed_z / 2.0F)->as_number(); // Do this to keep default settings the same + + this->slow_rate = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, slow_feedrate_checksum)->by_default(20.0F)->as_number(); + + this->numRows = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, rows_checksum)->by_default(5)->as_number(); + this->numCols = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, cols_checksum)->by_default(5)->as_number(); + + this->wait_for_probe = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, wait_for_probe_checksum)->by_default(true)->as_bool(); // Morgan default = true + + std::string home_mode = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, home_before_probe_checksum)->by_default("homexyz")->as_string(); + if (home_mode.compare("nohome") == 0) { + this->home_before_probe = NOHOME; + } + else if (home_mode.compare("homexy") == 0) { + this->home_before_probe = HOMEXY; + } + else { // Default + this->home_before_probe = HOMEXYZ; + } + + + this->center_zero = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, center_zero_checksum)->by_default(false)->as_bool(); + this->circular_bed = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, circular_bed_checksum)->by_default(false)->as_bool(); + + // configures calbration positioning offset. Defaults to 0 for standard cartesian space machines, and to negative half of the current bed size in X and Y + this->cal_offset_x = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, cal_offset_x_checksum)->by_default( this->center_zero ? this->bed_x / -2.0F : 0.0F )->as_number(); + this->cal_offset_y = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, cal_offset_y_checksum)->by_default( this->center_zero ? this->bed_y / -2.0F : 0.0F )->as_number(); + + + // Probe offsets xxx,yyy,zzz + std::string po = THEKERNEL->config->value(leveling_strategy_checksum, ZGrid_leveling_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string(); + this->probe_offsets= parseXYZ(po.c_str()); + + this->calcConfig(); // Run calculations for Grid size and allocate initial grid memory + + for (int i=0; i<(probe_points); i++){ + this->pData[i] = 0.0F; // Clear the grid + } + + return true; +} + +void ZGridStrategy::calcConfig() +{ + this->bed_div_x = this->bed_x / float(this->numRows-1); // Find divisors to calculate the calbration points + this->bed_div_y = this->bed_y / float(this->numCols-1); + + // Ensure free program memory for the pData grid + if(this->pData != nullptr) free(this->pData); + + // Allocate program memory for the pData grid + this->pData = (float *)malloc(probe_points * sizeof(float)); +} + +bool ZGridStrategy::handleGcode(Gcode *gcode) +{ + string args = get_arguments(gcode->get_command()); + + // G code processing + if(gcode->has_g) { + if( gcode->g == 31 ) { // report status + + // Bed ZGrid data as gcode: + gcode->stream->printf(";Bed Level settings:\r\n"); + + for (int x=0; xnumRows; x++){ + gcode->stream->printf("X%i",x); + for (int y=0; ynumCols; y++){ + gcode->stream->printf(" %c%1.2f", 'A'+y, this->pData[(x*this->numCols)+y]); + } + gcode->stream->printf("\r\n"); + } + return true; + + } else if( gcode->g == 32 ) { //run probe + // first wait for an empty queue i.e. no moves left + THEKERNEL->conveyor->wait_for_idle(); + + this->setAdjustFunction(false); // Disable leveling code + if(!doProbing(gcode->stream)) { + gcode->stream->printf("Probe failed to complete, probe not triggered or other error\n"); + } else { + this->setAdjustFunction(true); // Enable leveling code + gcode->stream->printf("Probe completed, bed grid defined\n"); + } + return true; + } + + } else if(gcode->has_m) { + switch( gcode->m ) { + + // manual bed ZGrid calbration: M370 - M375 + // M370: Clear current ZGrid for calibration, and move to first position + case 370: { + this->setAdjustFunction(false); // Disable leveling code + this->cal[Z_AXIS] = std::get(this->probe_offsets) + zprobe->getProbeHeight(); + + + if(gcode->has_letter('X')) // Rows (X) + this->numRows = gcode->get_value('X'); + if(gcode->has_letter('Y')) // Cols (Y) + this->numCols = gcode->get_value('Y'); + + this->calcConfig(); // Run calculations for Grid size and allocate grid memory + + this->homexyz(); + for (int i=0; ipData[i] = 0.0F; // Clear the ZGrid + } + + this->cal[X_AXIS] = 0.0f; // Clear calibration position + this->cal[Y_AXIS] = 0.0f; + this->in_cal = true; // In calbration mode + + } + return true; + // M371: Move to next manual calibration position + case 371: { + if (in_cal){ + this->move(this->cal, slow_rate); + this->next_cal(); + } + + } + return true; + // M372: save current position in ZGrid, and move to next calibration position + case 372: { + if (in_cal){ + float cartesian[3]; + int pindex = 0; + + THEROBOT->get_axis_position(cartesian); // get actual position from robot + + pindex = int(cartesian[X_AXIS]/this->bed_div_x + 0.25)*this->numCols + int(cartesian[Y_AXIS]/this->bed_div_y + 0.25); + + this->move(this->cal, slow_rate); // move to the next position + this->next_cal(); // to not cause damage to machine due to Z-offset + + this->pData[pindex] = cartesian[Z_AXIS]; // save the offset + + } + } + return true; + // M373: finalize calibration + case 373: { + // normalize the grid + this->normalize_grid_2home(); + + this->in_cal = false; + this->setAdjustFunction(true); // Enable leveling code + + } + return true; + + // M374: Save grid + case 374:{ + char gridname[5]; + + if(gcode->has_letter('S')) // Custom grid number + snprintf(gridname, sizeof(gridname), "S%03.0f", gcode->get_value('S')); + else + gridname[0] = '\0'; + + if(this->saveGrid(gridname)) { + gcode->stream->printf("Grid saved: Filename: /sd/Zgrid.%s\n",gridname); + } + else { + gcode->stream->printf("Error: Grid not saved: Filename: /sd/Zgrid.%s\n",gridname); + } + } + return true; + + case 375:{ // Load grid values + char gridname[5]; + + if(gcode->has_letter('S')) // Custom grid number + snprintf(gridname, sizeof(gridname), "S%03.0f", gcode->get_value('S')); + else + gridname[0] = '\0'; + + if(this->loadGrid(gridname)) { + this->setAdjustFunction(true); // Enable leveling code + gcode->stream->printf("Grid loaded: /sd/Zgrid.%s\n",gridname); + } + else { + gcode->stream->printf("Error: Grid not loaded: /sd/Zgrid.%s\n",gridname); + } + } + return true; + +/* case 376: { // Check grid value calculations: For debug only. + float target[3]; + + for(char letter = 'X'; letter <= 'Z'; letter++) { + if( gcode->has_letter(letter) ) { + target[letter - 'X'] = gcode->get_value(letter); + } + } + gcode->stream->printf(" Z0 %1.3f\n",getZOffset(target[0], target[1])); + + } + return true; +*/ + case 565: { // M565: Set Z probe offsets + float x= 0, y= 0, z= 0; + if(gcode->has_letter('X')) x = gcode->get_value('X'); + if(gcode->has_letter('Y')) y = gcode->get_value('Y'); + if(gcode->has_letter('Z')) z = gcode->get_value('Z'); + probe_offsets = std::make_tuple(x, y, z); + } + return true; + + case 500: // M500 saves probe_offsets config override file + gcode->stream->printf(";Load default grid\nM375\n"); + + + case 503: { // M503 just prints the settings + + float x,y,z; + gcode->stream->printf(";Probe offsets:\n"); + std::tie(x, y, z) = probe_offsets; + gcode->stream->printf("M565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z); + break; + } + + return true; + } + } + + return false; +} + + +bool ZGridStrategy::saveGrid(std::string args) +{ + args = "/sd/Zgrid." + args; + StreamOutput *ZMap_file = new FileStream(args.c_str()); + + ZMap_file->printf("P%i %i %i %1.3f\n", probe_points, this->numRows, this->numCols, getZhomeoffset()); // Store probe points to prevent loading undefined grid files + + for (int pos = 0; pos < probe_points; pos++){ + ZMap_file->printf("%1.3f\n", this->pData[pos]); + } + delete ZMap_file; + + return true; + +} + +bool ZGridStrategy::loadGrid(std::string args) +{ + char flag[20]; + + int fpoints, GridX = 5, GridY = 5; // for 25point file + float val, GridZ; + + args = "/sd/Zgrid." + args; + FILE *fd = fopen(args.c_str(), "r"); + if(fd != NULL) { + fscanf(fd, "%s\n", flag); + + if (flag[0] == 'P'){ + + sscanf(flag, "P%i\n", &fpoints); // read number of points, and Grid X and Y + fscanf(fd, "%i %i %f\n", &GridX, &GridY, &GridZ); // read number of points, and Grid X and Y and ZHoming offset + fscanf(fd, "%f\n", &val); // read first value from file + + } else { // original 25point file -- Backwards compatibility + fpoints = 25; + sscanf(flag, "%f\n", &val); // read first value from string + } + + if (GridX != this->numRows || GridY != this->numCols){ + this->numRows = GridX; // Change Rows and Columns to match the saved data + this->numCols = GridY; + this->calcConfig(); // Reallocate memory for the grid according to the grid loaded + } + + this->pData[0] = val; // Place the first read value in grid + + for (int pos = 1; pos < probe_points; pos++){ + fscanf(fd, "%f\n", &val); + this->pData[pos] = val; + } + + fclose(fd); + + this->setZoffset(GridZ); + + return true; + + } else { + return false; + } + +} + +float ZGridStrategy::getZhomeoffset() +{ + void* rd; + + bool ok = PublicData::get_value( endstops_checksum, home_offset_checksum, &rd ); + + if (ok) { + return ((float*)rd)[2]; + } + + return 0; +} + +void ZGridStrategy::setZoffset(float zval) +{ + char cmd[64]; + + // Assemble Gcode to add onto the queue + snprintf(cmd, sizeof(cmd), "M206 Z%1.3f", zval); // Send saved Z homing offset + + Gcode gc(cmd, &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc); + +} + +bool ZGridStrategy::doProbing(StreamOutput *stream) // probed calibration +{ + // home first using selected mode: NOHOME, HOMEXY, HOMEXYZ + this->homexyz(); + + // deactivate correction during moves + this->setAdjustFunction(false); + + for (int i=0; ipData[i] = 0.0F; // Clear the ZGrid + } + + if (this->wait_for_probe){ + + this->cal[X_AXIS] = this->probe_x; //bed_x/2.0f; + this->cal[Y_AXIS] = this->probe_y; //bed_y/2.0f; + this->cal[Z_AXIS] = this->probe_z; //bed_z/2.0f; // Position head for probe attachment + this->move(this->cal, slow_rate); // Move to probe attachment point + + stream->printf("*** Ensure probe is attached and press probe when done ***\n"); + + while(!zprobe->getProbeStatus()){ // Wait for button press + THEKERNEL->call_event(ON_IDLE); + }; + } + + this->in_cal = true; // In calbration mode + + this->cal[X_AXIS] = 0.0f; // Clear calibration position + this->cal[Y_AXIS] = 0.0f; + this->cal[Z_AXIS] = std::get(this->probe_offsets) + zprobe->getProbeHeight(); + + this->move(this->cal, slow_rate); // Move to probe start point + + for (int probes = 0; probes < probe_points; probes++){ + int pindex = 0; + + // z = z home offset - probed distance + float z = getZhomeoffset() -zprobe->probeDistance((this->cal[X_AXIS] + this->cal_offset_x)-std::get(this->probe_offsets), + (this->cal[Y_AXIS] + this->cal_offset_y)-std::get(this->probe_offsets)); + + pindex = int(this->cal[X_AXIS]/this->bed_div_x + 0.25)*this->numCols + int(this->cal[Y_AXIS]/this->bed_div_y + 0.25); + + this->next_cal(); // Calculate next calibration position + + this->pData[pindex] = z ; // save the offset + } + + stream->printf("\nCalibration done.\n"); + if (this->wait_for_probe) { // Only do this it the config calls for probe removal position + this->cal[X_AXIS] = this->bed_x/2.0f; + this->cal[Y_AXIS] = this->bed_y/2.0f; + this->cal[Z_AXIS] = this->bed_z/2.0f; // Position head for probe removal + this->move(this->cal, slow_rate); + + stream->printf("Please remove probe\n"); + + } + + // activate correction + //this->normalize_grid(); + this->normalize_grid_2home(); + this->setAdjustFunction(true); + + this->in_cal = false; + + return true; +} + + +void ZGridStrategy::normalize_grid_2home() +{ + void* rd; + float home_Z_comp; + + bool ok = PublicData::get_value( endstops_checksum, home_offset_checksum, &rd ); + + if (ok) { + home_Z_comp = this->getZOffset(((float*)rd)[0],((float*)rd)[1]); // find the Z compensation at home position + } + else { + home_Z_comp = 0; + } + + // subtracts the home compensation offset to create a table of deltas, normalized to home compensation zero + for (int i = 0; i < probe_points; i++) + this->pData[i] -= home_Z_comp; + + // Doing this removes the need to change homing offset in Z because the reference remains unchanged. + + // add the offset to the current Z homing offset to preserve full probed offset. + // this->setZoffset(this->getZhomeoffset() + home_Z_comp); + +} + +void ZGridStrategy::homexyz() +{ + + switch(this->home_before_probe) { + case NOHOME : return; + + case HOMEXY : { + Gcode gc("G28 X0 Y0", &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc); + break; + } + + case HOMEXYZ : { + Gcode gc("G28", &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc); + break; + } + } +} + +void ZGridStrategy::move(float *position, float feed) +{ + // translate the position for non standard cartesian spaces (cal_offset) + zprobe->coordinated_move(position[0] + this->cal_offset_x, position[1] + this->cal_offset_y, position[2], feed); // use specified feedrate (mm/sec) + + //THEKERNEL->streams->printf("DEBUG: move: %s cent: %i\n", cmd, this->center_zero); +} + + +void ZGridStrategy::next_cal(void){ + if ((((int) roundf(this->cal[X_AXIS] / this->bed_div_x)) & 1) != 0){ // Odd row + this->cal[Y_AXIS] -= this->bed_div_y; + if (this->cal[Y_AXIS] < (0.0F - (bed_div_y / 2.0f))){ + + //THEKERNEL->streams->printf("DEBUG: Y (%f) < cond (%f)\n",this->cal[Y_AXIS], 0.0F); + + this->cal[X_AXIS] += bed_div_x; + if (this->cal[X_AXIS] > (this->bed_x + (this->bed_div_x / 2.0f))){ + this->cal[X_AXIS] = 0.0F; + this->cal[Y_AXIS] = 0.0F; + } + else + this->cal[Y_AXIS] = 0.0F; + } + } + else { // Even row (0 is an even row - starting point) + this->cal[Y_AXIS] += bed_div_y; + if (this->cal[Y_AXIS] > (this->bed_y + (bed_div_y / 2.0f))){ + + //THEKERNEL->streams->printf("DEBUG: Y (%f) > cond (%f)\n",this->cal[Y_AXIS], this->bed_y); + + this->cal[X_AXIS] += bed_div_x; + if (this->cal[X_AXIS] > (this->bed_x + (this->bed_div_x / 2.0f))){ + this->cal[X_AXIS] = 0.0F; + this->cal[Y_AXIS] = 0.0F; + } + else + this->cal[Y_AXIS] = this->bed_y; + } + } +} + + +void ZGridStrategy::setAdjustFunction(bool on) +{ + if(on) { + // set the compensationTransform in robot + THEROBOT->compensationTransform= [this](float target[3]) { target[2] += this->getZOffset(target[0], target[1]); }; + }else{ + // clear it + THEROBOT->compensationTransform= nullptr; + } +} + + +// find the Z offset for the point on the plane at x, y +float ZGridStrategy::getZOffset(float X, float Y) +{ + int xIndex2, yIndex2; + + // Subtract calibration offsets as applicable + X -= this->cal_offset_x; + Y -= this->cal_offset_y; + + float xdiff = X / this->bed_div_x; + float ydiff = Y / this->bed_div_y; + + float dCartX1, dCartX2; + + // Get floor of xdiff. Note that (int) of a negative number is its + // ceiling, not its floor. + + int xIndex = (int)(floorf(xdiff)); // Get the current sector (X) + int yIndex = (int)(floorf(ydiff)); // Get the current sector (Y) + + // Index bounds limited to be inside the table + if (xIndex < 0) xIndex = 0; + else if (xIndex > (this->numRows - 2)) xIndex = this->numRows - 2; + + if (yIndex < 0) yIndex = 0; + else if (yIndex > (this->numCols - 2)) yIndex = this->numCols - 2; + + xIndex2 = xIndex+1; + yIndex2 = yIndex+1; + + xdiff -= xIndex; // Find floating point + ydiff -= yIndex; // Find floating point + + dCartX1 = (1-xdiff) * this->pData[(xIndex*this->numCols)+yIndex] + (xdiff) * this->pData[(xIndex2)*this->numCols+yIndex]; + dCartX2 = (1-xdiff) * this->pData[(xIndex*this->numCols)+yIndex2] + (xdiff) * this->pData[(xIndex2)*this->numCols+yIndex2]; + + return ydiff * dCartX2 + (1-ydiff) * dCartX1; // Calculated Z-delta + +} + +// parse a "X,Y,Z" string return x,y,z tuple +std::tuple ZGridStrategy::parseXYZ(const char *str) +{ + float x = 0, y = 0, z= 0; + char *p; + x = strtof(str, &p); + if(p + 1 < str + strlen(str)) { + y = strtof(p + 1, &p); + if(p + 1 < str + strlen(str)) { + z = strtof(p + 1, nullptr); + } + } + return std::make_tuple(x, y, z); +} + diff --git a/src/modules/tools/zprobe/ZGridStrategy.h b/src/modules/tools/zprobe/ZGridStrategy.h new file mode 100644 index 00000000..13be866e --- /dev/null +++ b/src/modules/tools/zprobe/ZGridStrategy.h @@ -0,0 +1,66 @@ +#ifndef _ZGridSTRATEGY +#define _ZGridSTRATEGY + +#include "LevelingStrategy.h" + +#include +#include +#include + +#define ZGrid_leveling_checksum CHECKSUM("ZGrid-leveling") + +class StreamOutput; + +class ZGridStrategy : public LevelingStrategy +{ +public: + ZGridStrategy(ZProbe *zprobe); + ~ZGridStrategy(); + bool handleGcode(Gcode* gcode); + bool handleConfig(); + float getZOffset(float x, float y); + +private: + void homexyz(); + + void move(float *position, float feed); + void next_cal(void); + float getZhomeoffset(); + void setZoffset(float zval); + + void setAdjustFunction(bool); + bool doProbing(StreamOutput *stream); + void normalize_grid_2home(); + + bool loadGrid(std::string args); + bool saveGrid(std::string args); + void calcConfig(); + + std::tuple probe_offsets; + std::tuple parseXYZ(const char *str); + + uint16_t numRows; + uint16_t numCols; + float *pData; + float slow_rate; + float bed_x; + float bed_y; + float bed_z; + float probe_x; + float probe_y; + float probe_z; + float cal_offset_x; + float cal_offset_y; + float bed_div_x; + float bed_div_y; + float cal[3]; // calibration positions for manual leveling + struct { + char home_before_probe:4; + bool in_cal:1; + bool center_zero:1; + bool circular_bed:1; + bool wait_for_probe:1; + }; +}; + +#endif diff --git a/src/modules/tools/zprobe/ZProbe.cpp b/src/modules/tools/zprobe/ZProbe.cpp new file mode 100644 index 00000000..9fe64b70 --- /dev/null +++ b/src/modules/tools/zprobe/ZProbe.cpp @@ -0,0 +1,531 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#include "ZProbe.h" + +#include "Kernel.h" +#include "BaseSolution.h" +#include "Config.h" +#include "Robot.h" +#include "StepperMotor.h" +#include "StreamOutputPool.h" +#include "Gcode.h" +#include "Conveyor.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "SlowTicker.h" +#include "Planner.h" +#include "SerialMessage.h" +#include "PublicDataRequest.h" +#include "EndstopsPublicAccess.h" +#include "PublicData.h" +#include "LevelingStrategy.h" +#include "StepTicker.h" +#include "utils.h" + +// strategies we know about +#include "DeltaCalibrationStrategy.h" +#include "ThreePointStrategy.h" +//#include "ZGridStrategy.h" +#include "DeltaGridStrategy.h" + +#define enable_checksum CHECKSUM("enable") +#define probe_pin_checksum CHECKSUM("probe_pin") +#define debounce_ms_checksum CHECKSUM("debounce_ms") +#define slow_feedrate_checksum CHECKSUM("slow_feedrate") +#define fast_feedrate_checksum CHECKSUM("fast_feedrate") +#define return_feedrate_checksum CHECKSUM("return_feedrate") +#define probe_height_checksum CHECKSUM("probe_height") +#define gamma_max_checksum CHECKSUM("gamma_max") +#define reverse_z_direction_checksum CHECKSUM("reverse_z") + +// from endstop section +#define delta_homing_checksum CHECKSUM("delta_homing") +#define rdelta_homing_checksum CHECKSUM("rdelta_homing") + +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 + +#define STEPPER THEROBOT->actuators +#define STEPS_PER_MM(a) (STEPPER[a]->get_steps_per_mm()) +#define Z_STEPS_PER_MM STEPS_PER_MM(Z_AXIS) + +#define abs(a) ((a<0) ? -a : a) + +void ZProbe::on_module_loaded() +{ + // if the module is disabled -> do nothing + if(!THEKERNEL->config->value( zprobe_checksum, enable_checksum )->by_default(false)->as_bool()) { + // as this module is not needed free up the resource + delete this; + return; + } + + // load settings + this->config_load(); + // register event-handlers + register_for_event(ON_GCODE_RECEIVED); + + // we read the probe in this timer, currently only for G38 probes. + probing= false; + THEKERNEL->slow_ticker->attach(1000, this, &ZProbe::read_probe); +} + +void ZProbe::config_load() +{ + this->pin.from_string( THEKERNEL->config->value(zprobe_checksum, probe_pin_checksum)->by_default("nc" )->as_string())->as_input(); + this->debounce_ms = THEKERNEL->config->value(zprobe_checksum, debounce_ms_checksum)->by_default(0 )->as_number(); + + // get strategies to load + vector modules; + THEKERNEL->config->get_module_list( &modules, leveling_strategy_checksum); + for( auto cs : modules ){ + if( THEKERNEL->config->value(leveling_strategy_checksum, cs, enable_checksum )->as_bool() ){ + bool found= false; + // check with each known strategy and load it if it matches + switch(cs) { + case delta_calibration_strategy_checksum: + this->strategies.push_back(new DeltaCalibrationStrategy(this)); + found= true; + break; + + case three_point_leveling_strategy_checksum: + // NOTE this strategy is mutually exclusive with the delta calibration strategy + this->strategies.push_back(new ThreePointStrategy(this)); + found= true; + break; + + // case ZGrid_leveling_checksum: + // this->strategies.push_back(new ZGridStrategy(this)); + // found= true; + // break; + + case delta_grid_leveling_strategy_checksum: + this->strategies.push_back(new DeltaGridStrategy(this)); + found= true; + break; + } + if(found) this->strategies.back()->handleConfig(); + } + } + + // need to know if we need to use delta kinematics for homing + this->is_delta = THEKERNEL->config->value(delta_homing_checksum)->by_default(false)->as_bool(); + this->is_rdelta = THEKERNEL->config->value(rdelta_homing_checksum)->by_default(false)->as_bool(); + + // default for backwards compatibility add DeltaCalibrationStrategy if a delta + // will be deprecated + if(this->strategies.empty()) { + if(this->is_delta) { + this->strategies.push_back(new DeltaCalibrationStrategy(this)); + this->strategies.back()->handleConfig(); + } + } + + this->probe_height = THEKERNEL->config->value(zprobe_checksum, probe_height_checksum)->by_default(5.0F)->as_number(); + this->slow_feedrate = THEKERNEL->config->value(zprobe_checksum, slow_feedrate_checksum)->by_default(5)->as_number(); // feedrate in mm/sec + this->fast_feedrate = THEKERNEL->config->value(zprobe_checksum, fast_feedrate_checksum)->by_default(100)->as_number(); // feedrate in mm/sec + this->return_feedrate = THEKERNEL->config->value(zprobe_checksum, return_feedrate_checksum)->by_default(0)->as_number(); // feedrate in mm/sec + this->reverse_z = THEKERNEL->config->value(zprobe_checksum, reverse_z_direction_checksum)->by_default(false)->as_bool(); // Z probe moves in reverse direction + this->max_z = THEKERNEL->config->value(gamma_max_checksum)->by_default(500)->as_number(); // maximum zprobe distance +} + +uint32_t ZProbe::read_probe(uint32_t dummy) +{ + if(!probing || probe_detected) return 0; + + if(STEPPER[Z_AXIS]->is_moving()) { + // if it is moving then we check the probe, and debounce it + if(this->pin.get()) { + if(debounce < debounce_ms) { + debounce++; + } else { + // we signal the motors to stop, which will preempt any moves on that axis + // we do all motors as it may be a delta + for(auto &a : THEROBOT->actuators) a->stop_moving(); + probe_detected= true; + debounce= 0; + } + + } else { + // The endstop was not hit yet + debounce= 0; + } + } + + return 0; +} + +// single probe in Z with custom feedrate +// returns boolean value indicating if probe was triggered +bool ZProbe::run_probe(float& mm, float feedrate, float max_dist, bool reverse) +{ + float maxz= max_dist < 0 ? this->max_z*2 : max_dist; + + probing= true; + probe_detected= false; + debounce= 0; + + // save current actuator position so we can report how far we moved + ActuatorCoordinates start_pos{ + THEROBOT->actuators[X_AXIS]->get_current_position(), + THEROBOT->actuators[Y_AXIS]->get_current_position(), + THEROBOT->actuators[Z_AXIS]->get_current_position() + }; + + // move Z down + THEROBOT->disable_segmentation= true; // we must disable segmentation as this won't work with it enabled + bool dir= (!reverse_z != reverse); // xor + float delta[3]= {0,0,0}; + delta[Z_AXIS]= dir ? -maxz : maxz; + THEROBOT->delta_move(delta, feedrate, 3); + + // wait until finished + THECONVEYOR->wait_for_idle(); + THEROBOT->disable_segmentation= false; + + // now see how far we moved, get delta in z we moved + // NOTE this works for deltas as well as all three actuators move the same amount in Z + mm= start_pos[2] - THEROBOT->actuators[2]->get_current_position(); + + // set the last probe position to the actuator units moved during this home + THEROBOT->set_last_probe_position( + std::make_tuple( + start_pos[0] - THEROBOT->actuators[0]->get_current_position(), + start_pos[1] - THEROBOT->actuators[1]->get_current_position(), + mm, + probe_detected?1:0)); + + probing= false; + + if(probe_detected) { + // if the probe stopped the move we need to correct the last_milestone as it did not reach where it thought + THEROBOT->reset_position_from_current_actuator_position(); + } + + return probe_detected; +} + +bool ZProbe::return_probe(float mm, bool reverse) +{ + // move probe back to where it was + float fr; + if(this->return_feedrate != 0) { // use return_feedrate if set + fr = this->return_feedrate; + } else { + fr = this->slow_feedrate*2; // nominally twice slow feedrate + if(fr > this->fast_feedrate) fr = this->fast_feedrate; // unless that is greater than fast feedrate + } + + bool dir= ((mm < 0) != reverse_z); // xor + if(reverse) dir= !dir; + + float delta[3]= {0,0,0}; + delta[Z_AXIS]= dir ? -mm : mm; + THEROBOT->delta_move(delta, fr, 3); + + // wait until finished + THECONVEYOR->wait_for_idle(); + + return true; +} + +bool ZProbe::doProbeAt(float &mm, float x, float y) +{ + float s; + // move to xy + coordinated_move(x, y, NAN, getFastFeedrate()); + if(!run_probe(s)) return false; + + // return to original Z + return_probe(s); + mm = s; + + return true; +} + +float ZProbe::probeDistance(float x, float y) +{ + float s; + if(!doProbeAt(s, x, y)) return NAN; + return s; +} + +void ZProbe::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + + if( gcode->has_g && gcode->g >= 29 && gcode->g <= 32) { + + // make sure the probe is defined and not already triggered before moving motors + if(!this->pin.connected()) { + gcode->stream->printf("ZProbe not connected.\n"); + return; + } + if(this->pin.get()) { + gcode->stream->printf("ZProbe triggered before move, aborting command.\n"); + return; + } + + if( gcode->g == 30 ) { // simple Z probe + // first wait for an empty queue i.e. no moves left + THEKERNEL->conveyor->wait_for_idle(); + + // turn off any compensation transform + auto savect= THEROBOT->compensationTransform; + THEROBOT->compensationTransform= nullptr; + + bool probe_result; + bool reverse= (gcode->has_letter('R') && gcode->get_value('R') != 0); // specify to probe in reverse direction + float rate= gcode->has_letter('F') ? gcode->get_value('F') / 60 : this->slow_feedrate; + float mm; + probe_result = run_probe(mm, rate, -1, reverse); + + if(probe_result) { + // the result is in actuator coordinates and raw steps + gcode->stream->printf("Z:%1.4f\n", mm); + + // set the last probe position to the current actuator units + THEROBOT->set_last_probe_position(std::make_tuple( + THEROBOT->actuators[X_AXIS]->get_current_position(), + THEROBOT->actuators[Y_AXIS]->get_current_position(), + THEROBOT->actuators[Z_AXIS]->get_current_position(), + 1)); + + // move back to where it started, unless a Z is specified (and not a rotary delta) + if(gcode->has_letter('Z') && !is_rdelta) { + // set Z to the specified value, and leave probe where it is + THEROBOT->reset_axis_position(gcode->get_value('Z'), Z_AXIS); + + } else { + // return to pre probe position + return_probe(mm, reverse); + } + + } else { + gcode->stream->printf("ZProbe not triggered\n"); + THEROBOT->set_last_probe_position(std::make_tuple( + THEROBOT->actuators[X_AXIS]->get_current_position(), + THEROBOT->actuators[Y_AXIS]->get_current_position(), + THEROBOT->actuators[Z_AXIS]->get_current_position(), + 0)); + } + + // restore compensationTransform + THEROBOT->compensationTransform= savect; + + } else { + if(!gcode->has_letter('P')) { + // find the first strategy to handle the gcode + for(auto s : strategies){ + if(s->handleGcode(gcode)) { + return; + } + } + gcode->stream->printf("No strategy found to handle G%d\n", gcode->g); + + }else{ + // P paramater selects which strategy to send the code to + // they are loaded in the order they are defined in config, 0 being the first, 1 being the second and so on. + uint16_t i= gcode->get_value('P'); + if(i < strategies.size()) { + if(!strategies[i]->handleGcode(gcode)){ + gcode->stream->printf("strategy #%d did not handle G%d\n", i, gcode->g); + } + return; + + }else{ + gcode->stream->printf("strategy #%d is not loaded\n", i); + } + } + } + + } else if(gcode->has_g && gcode->g == 38 ) { // G38.2 Straight Probe with error, G38.3 straight probe without error + // linuxcnc/grbl style probe http://www.linuxcnc.org/docs/2.5/html/gcode/gcode.html#sec:G38-probe + if(gcode->subcode != 2 && gcode->subcode != 3) { + gcode->stream->printf("error:Only G38.2 and G38.3 are supported\n"); + return; + } + + // make sure the probe is defined and not already triggered before moving motors + if(!this->pin.connected()) { + gcode->stream->printf("error:ZProbe not connected.\n"); + return; + } + + if(this->pin.get()) { + gcode->stream->printf("error:ZProbe triggered before move, aborting command.\n"); + return; + } + + // first wait for an empty queue i.e. no moves left + THEKERNEL->conveyor->wait_for_idle(); + + // turn off any compensation transform + auto savect= THEROBOT->compensationTransform; + THEROBOT->compensationTransform= nullptr; + + if(gcode->has_letter('X')) { + // probe in the X axis + probe_XYZ(gcode, X_AXIS); + + }else if(gcode->has_letter('Y')) { + // probe in the Y axis + probe_XYZ(gcode, Y_AXIS); + + }else if(gcode->has_letter('Z')) { + // probe in the Z axis + probe_XYZ(gcode, Z_AXIS); + + }else{ + gcode->stream->printf("error:at least one of X Y or Z must be specified\n"); + } + + // restore compensationTransform + THEROBOT->compensationTransform= savect; + + return; + + } else if(gcode->has_m) { + // M code processing here + int c; + switch (gcode->m) { + case 119: + c = this->pin.get(); + gcode->stream->printf(" Probe: %d", c); + gcode->add_nl = true; + break; + + case 670: + if (gcode->has_letter('S')) this->slow_feedrate = gcode->get_value('S'); + if (gcode->has_letter('K')) this->fast_feedrate = gcode->get_value('K'); + if (gcode->has_letter('R')) this->return_feedrate = gcode->get_value('R'); + if (gcode->has_letter('Z')) this->max_z = gcode->get_value('Z'); + if (gcode->has_letter('H')) this->probe_height = gcode->get_value('H'); + if (gcode->has_letter('I')) { // NOTE this is temporary and toggles the invertion status of the pin + invert_override= (gcode->get_value('I') != 0); + pin.set_inverting(pin.is_inverting() != invert_override); // XOR so inverted pin is not inverted and vice versa + } + break; + + case 500: // save settings + case 503: // print settings + gcode->stream->printf(";Probe feedrates Slow/fast(K)/Return (mm/sec) max_z (mm) height (mm):\nM670 S%1.2f K%1.2f R%1.2f Z%1.2f H%1.2f\n", + this->slow_feedrate, this->fast_feedrate, this->return_feedrate, this->max_z, this->probe_height); + + // fall through is intended so leveling strategies can handle m-codes too + + default: + for(auto s : strategies){ + if(s->handleGcode(gcode)) { + return; + } + } + } + } +} + +// special way to probe in the X or Y or Z direction using planned moves, should work with any kinematics +void ZProbe::probe_XYZ(Gcode *gcode, int axis) +{ + // enable the probe checking in the timer + probing= true; + probe_detected= false; + THEROBOT->disable_segmentation= true; // we must disable segmentation as this won't work with it enabled (beware on deltas probing in X or Y) + + // get probe feedrate if specified + float rate = (gcode->has_letter('F')) ? gcode->get_value('F')*60 : this->slow_feedrate; + + // do a regular move which will stop as soon as the probe is triggered, or the distance is reached + switch(axis) { + case X_AXIS: coordinated_move(gcode->get_value('X'), 0, 0, rate, true); break; + case Y_AXIS: coordinated_move(0, gcode->get_value('Y'), 0, rate, true); break; + case Z_AXIS: coordinated_move(0, 0, gcode->get_value('Z'), rate, true); break; + } + + // coordinated_move returns when the move is finished + + // disable probe checking + probing= false; + THEROBOT->disable_segmentation= false; + + float pos[3]; + { + // get the current position + ActuatorCoordinates current_position{ + THEROBOT->actuators[X_AXIS]->get_current_position(), + THEROBOT->actuators[Y_AXIS]->get_current_position(), + THEROBOT->actuators[Z_AXIS]->get_current_position() + }; + + // get machine position from the actuator position using FK + THEROBOT->arm_solution->actuator_to_cartesian(current_position, pos); + } + + uint8_t probeok= this->probe_detected ? 1 : 0; + + // print results using the GRBL format + gcode->stream->printf("[PRB:%1.3f,%1.3f,%1.3f:%d]\n", pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS], probeok); + THEROBOT->set_last_probe_position(std::make_tuple(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS], probeok)); + + if(!probeok && gcode->subcode == 2) { + // issue error if probe was not triggered and subcode == 2 + gcode->stream->printf("ALARM:Probe fail\n"); + THEKERNEL->call_event(ON_HALT, nullptr); + + }else if(probeok){ + // if the probe stopped the move we need to correct the last_milestone as it did not reach where it thought + THEROBOT->reset_position_from_current_actuator_position(); + } +} + +// issue a coordinated move directly to robot, and return when done +// Only move the coordinates that are passed in as not nan +// NOTE must use G53 to force move in machine coordinates and ignore any WCS offsets +void ZProbe::coordinated_move(float x, float y, float z, float feedrate, bool relative) +{ + char buf[32]; + char cmd[64]; + + if(relative) strcpy(cmd, "G91 G0 "); + else strcpy(cmd, "G53 G0 "); // G53 forces movement in machine coordinate system + + if(!isnan(x)) { + int n = snprintf(buf, sizeof(buf), " X%1.3f", x); + strncat(cmd, buf, n); + } + if(!isnan(y)) { + int n = snprintf(buf, sizeof(buf), " Y%1.3f", y); + strncat(cmd, buf, n); + } + if(!isnan(z)) { + int n = snprintf(buf, sizeof(buf), " Z%1.3f", z); + strncat(cmd, buf, n); + } + + // use specified feedrate (mm/sec) + int n = snprintf(buf, sizeof(buf), " F%1.1f", feedrate * 60); // feed rate is converted to mm/min + strncat(cmd, buf, n); + if(relative) strcat(cmd, " G90"); + + //THEKERNEL->streams->printf("DEBUG: move: %s\n", cmd); + + // send as a command line as may have multiple G codes in it + struct SerialMessage message; + message.message = cmd; + message.stream = &(StreamOutput::NullStream); + THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message ); + THEKERNEL->conveyor->wait_for_idle(); +} + +// issue home command +void ZProbe::home() +{ + Gcode gc("G28", &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc); +} diff --git a/src/modules/tools/zprobe/ZProbe.h b/src/modules/tools/zprobe/ZProbe.h new file mode 100644 index 00000000..7f4324a1 --- /dev/null +++ b/src/modules/tools/zprobe/ZProbe.h @@ -0,0 +1,75 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#ifndef ZPROBE_H_ +#define ZPROBE_H_ + +#include "Module.h" +#include "Pin.h" + +#include + +// defined here as they are used in multiple files +#define zprobe_checksum CHECKSUM("zprobe") +#define leveling_strategy_checksum CHECKSUM("leveling-strategy") + +class StepperMotor; +class Gcode; +class StreamOutput; +class LevelingStrategy; + +class ZProbe: public Module +{ + +public: + ZProbe() : invert_override(false) {}; + virtual ~ZProbe() {}; + + void on_module_loaded(); + void on_gcode_received(void *argument); + + bool run_probe(float& mm, float feedrate, float max_dist= -1, bool reverse= false); + bool run_probe(float& mm, bool fast= false) { return run_probe(mm, fast ? this->fast_feedrate : this->slow_feedrate); } + bool return_probe(float mm, bool reverse= false); + bool doProbeAt(float &mm, float x, float y); + float probeDistance(float x, float y); + + void coordinated_move(float x, float y, float z, float feedrate, bool relative=false); + void home(); + + bool getProbeStatus() { return this->pin.get(); } + float getSlowFeedrate() const { return slow_feedrate; } + float getFastFeedrate() const { return fast_feedrate; } + float getProbeHeight() const { return probe_height; } + float getMaxZ() const { return max_z; } + +private: + void config_load(); + void probe_XYZ(Gcode *gc, int axis); + uint32_t read_probe(uint32_t dummy); + + float slow_feedrate; + float fast_feedrate; + float return_feedrate; + float probe_height; + float max_z; + + Pin pin; + std::vector strategies; + uint16_t debounce_ms, debounce; + + volatile struct { + bool is_delta:1; + bool is_rdelta:1; + bool probing:1; + bool reverse_z:1; + bool invert_override:1; + volatile bool probe_detected:1; + }; +}; + +#endif /* ZPROBE_H_ */ diff --git a/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp index 0c432938..db26ef76 100644 --- a/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp +++ b/src/modules/utils/motordrivercontrol/drivers/TMC26X/TMC26X.cpp @@ -38,7 +38,6 @@ #include "Config.h" #include "checksumm.h" - #define motor_driver_control_checksum CHECKSUM("motor_driver_control") #define sense_resistor_checksum CHECKSUM("sense_resistor") From fc29c160a99daf37c46c1bad836afcb55d0d0c5d Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Fri, 28 Oct 2016 10:59:00 +1300 Subject: [PATCH 24/66] modified: src/main.cpp modified: src/makefile --- src/main.cpp | 19 +++++++++++++++++++ src/makefile | 26 +++++++++++++++++++------- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6e70e8dd..6edae83c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,25 @@ #include "Endstops.h" #include "Laser.h" #include "Config.h" +#include "checksumm.h" +#include "ConfigValue.h" +#include "StepTicker.h" +#include "SlowTicker.h" +#include "Robot.h" + +// #include "libs/ChaNFSSD/SDFileSystem.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" + +// Debug +#include "libs/SerialMessage.h" + +//#include "libs/USBDevice/USB.h" +//#include "libs/USBDevice/USBMSD/USBMSD.h" +//#include "libs/USBDevice/USBMSD/SDCard.h" +//#include "libs/USBDevice/USBSerial/USBSerial.h" +//#include "libs/USBDevice/DFU.h" +//#include "libs/SDFAT.h" #include "StreamOutputPool.h" #include "MotorDriverControl.h" #include "SEGGER_SYSVIEW.h" diff --git a/src/makefile b/src/makefile index e25a6c00..553e921c 100644 --- a/src/makefile +++ b/src/makefile @@ -15,7 +15,17 @@ MRI_BREAK_ON_INIT ?= 1 # time sensitive. #OPTIMIZATION := 0 - +# Set to non zero value if you want checks to be enabled which reserve a +# specific amount of space for the stack. The heap's growth will be +# constrained to reserve this much space for the stack and the stack won't be +# able to grow larger than this amount. +STACK_SIZE=3072 + +# Set to 1 to allow MRI debug monitor to take full control of UART0 and use it +# as a dedicated debug channel. If you are using the USB based serial port for +# the console then this should cause you no problems. Set MRI_BREAK_ON_INIT to +# 0 if you don't want to break into GDB at startup. +ENABLE_DEBUG_MONITOR?=0 ######################################################################################################################## PROJECT := Smoothie2 @@ -80,16 +90,18 @@ GCC4MBED_TYPE := Checked else GCC4MBED_TYPE := Release endif - -# generate a git version string -ifneq "$(OS)" "Windows_NT" -DEFINES += -D__GITVERSIONSTRING__=\"$(shell ./generate-version.sh)\" -else -DEFINES += -D__GITVERSIONSTRING__=\"$(shell generate-version.bat)\" + +# generate a git version string +ifneq "$(OS)" "Windows_NT" +DEFINES += -D__GITVERSIONSTRING__=\"$(shell ./generate-version.sh)\" +else +DEFINES += -D__GITVERSIONSTRING__=\"$(shell generate-version.bat)\" endif include $(GCC4MBED_DIR)/build/gcc4mbed.mk +# set to not compile in any network support +#export NONETWORK = 1 configdefault.o : config.default @echo Packing $< From 05ed230da5c5f863eb42f646800bc576954f08a7 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 30 Oct 2016 17:43:35 +1300 Subject: [PATCH 25/66] modified: libs/Kernel.cpp modified: libs/Kernel.h modified: libs/StepTicker.cpp modified: libs/StepTicker.h modified: libs/StepperMotor.cpp modified: libs/StepperMotor.h modified: makefile modified: modules/communication/GcodeDispatch.cpp modified: modules/communication/utils/Gcode.cpp modified: modules/communication/utils/Gcode.h modified: modules/robot/ActuatorCoordinates.h modified: modules/robot/Block.cpp modified: modules/robot/Block.h modified: modules/robot/Robot.cpp modified: modules/robot/Robot.h modified: modules/robot/arm_solutions/BaseSolution.h modified: modules/robot/arm_solutions/CartesianSolution.cpp modified: modules/robot/arm_solutions/CartesianSolution.h modified: modules/robot/arm_solutions/CoreXZSolution.cpp modified: modules/robot/arm_solutions/CoreXZSolution.h modified: modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp modified: modules/robot/arm_solutions/ExperimentalDeltaSolution.h modified: modules/robot/arm_solutions/HBotSolution.cpp modified: modules/robot/arm_solutions/HBotSolution.h modified: modules/robot/arm_solutions/LinearDeltaSolution.cpp modified: modules/robot/arm_solutions/LinearDeltaSolution.h modified: modules/robot/arm_solutions/MorganSCARASolution.cpp modified: modules/robot/arm_solutions/MorganSCARASolution.h modified: modules/robot/arm_solutions/RotaryDeltaSolution.cpp modified: modules/robot/arm_solutions/RotaryDeltaSolution.h modified: modules/robot/arm_solutions/RotatableCartesianSolution.cpp modified: modules/robot/arm_solutions/RotatableCartesianSolution.h modified: modules/tools/drillingcycles/Drillingcycles.cpp modified: modules/tools/drillingcycles/Drillingcycles.h --- src/libs/Kernel.cpp | 113 ++++++++++++++---- src/libs/Kernel.h | 10 +- src/libs/StepTicker.cpp | 30 ++--- src/libs/StepTicker.h | 12 +- src/libs/StepperMotor.cpp | 30 ++++- src/libs/StepperMotor.h | 17 +-- src/makefile | 4 + src/modules/communication/GcodeDispatch.cpp | 9 +- src/modules/communication/utils/Gcode.cpp | 6 + src/modules/communication/utils/Gcode.h | 1 + src/modules/robot/ActuatorCoordinates.h | 10 +- src/modules/robot/Block.cpp | 0 src/modules/robot/Block.h | 0 src/modules/robot/Robot.cpp | 1 - src/modules/robot/Robot.h | 15 +-- .../robot/arm_solutions/BaseSolution.h | 7 +- .../robot/arm_solutions/CartesianSolution.cpp | 4 +- .../robot/arm_solutions/CartesianSolution.h | 15 ++- .../robot/arm_solutions/CoreXZSolution.cpp | 4 +- .../robot/arm_solutions/CoreXZSolution.h | 10 +- .../ExperimentalDeltaSolution.cpp | 16 +-- .../arm_solutions/ExperimentalDeltaSolution.h | 18 ++- .../robot/arm_solutions/HBotSolution.cpp | 6 +- .../robot/arm_solutions/HBotSolution.h | 16 ++- .../arm_solutions/LinearDeltaSolution.cpp | 25 ++-- .../robot/arm_solutions/LinearDeltaSolution.h | 11 +- .../arm_solutions/MorganSCARASolution.cpp | 89 +++++++------- .../robot/arm_solutions/MorganSCARASolution.h | 8 +- .../arm_solutions/RotaryDeltaSolution.cpp | 10 +- .../robot/arm_solutions/RotaryDeltaSolution.h | 15 +-- .../RotatableCartesianSolution.cpp | 12 +- .../RotatableCartesianSolution.h | 14 ++- .../tools/drillingcycles/Drillingcycles.cpp | 2 + .../tools/drillingcycles/Drillingcycles.h | 0 34 files changed, 312 insertions(+), 228 deletions(-) mode change 100644 => 100755 src/modules/communication/utils/Gcode.cpp mode change 100644 => 100755 src/modules/communication/utils/Gcode.h mode change 100644 => 100755 src/modules/robot/ActuatorCoordinates.h mode change 100644 => 100755 src/modules/robot/Block.cpp mode change 100644 => 100755 src/modules/robot/Block.h mode change 100644 => 100755 src/modules/robot/Robot.cpp mode change 100644 => 100755 src/modules/robot/Robot.h mode change 100644 => 100755 src/modules/robot/arm_solutions/BaseSolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/CartesianSolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/CartesianSolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/CoreXZSolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/CoreXZSolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/HBotSolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/HBotSolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/LinearDeltaSolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/LinearDeltaSolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/MorganSCARASolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/MorganSCARASolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/RotaryDeltaSolution.h mode change 100644 => 100755 src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp mode change 100644 => 100755 src/modules/robot/arm_solutions/RotatableCartesianSolution.h mode change 100644 => 100755 src/modules/tools/drillingcycles/Drillingcycles.cpp mode change 100644 => 100755 src/modules/tools/drillingcycles/Drillingcycles.h diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 00321421..bcfd0a55 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -26,7 +26,7 @@ #include "StepperMotor.h" #include "BaseSolution.h" #include "EndstopsPublicAccess.h" -//#include "Configurator.h" +#include "Configurator.h" //#include "SimpleShell.h" //#include "platform_memory.h" @@ -48,8 +48,6 @@ Kernel* Kernel::instance; // The kernel is the central point in Smoothie : it stores modules, and handles event calls Kernel::Kernel(){ - - debug = false; halted= false; feed_hold= false; @@ -58,34 +56,68 @@ Kernel::Kernel(){ // All streams register to the Kernel so we can broadcast messages this->streams = new StreamOutputPool(); - this->serial = nullptr; - this->secondary_serial = nullptr; +#ifndef SMOOTHIE_UART_PRIMARY_ENABLE +#define SMOOTHIE_UART_PRIMARY_ENABLE 0 +#endif - // Create the default UART Serial Console interface -#ifdef SMOOTHIE_UART_PRIMARY_ENABLE - this->serial = new SerialConsole(SMOOTHIE_UART_PRIMARY_TX, SMOOTHIE_UART_PRIMARY_RX, 9600); - this->add_module( this->serial ); +#ifndef SMOOTHIE_UART_SECONDARY_ENABLE +#define SMOOTHIE_UART_SECONDARY_ENABLE 0 #endif -#ifdef SMOOTHIE_UART_SECONDARY_ENABLE - this->secondary_serial = new SerialConsole(SMOOTHIE_UART_SECONDARY_TX, SMOOTHIE_UART_SECONDARY_RX, 9600); - this->add_module( this->secondary_serial ); +#ifndef DEFAULT_SERIAL_BAUD_RATE +#define DEFAULT_SERIAL_BAUD_RATE 9600 #endif + + // Create the default UART Serial Console interface + if (SMOOTHIE_UART_PRIMARY_ENABLE) { + this->serial = new SerialConsole(SMOOTHIE_UART_PRIMARY_TX, SMOOTHIE_UART_PRIMARY_RX, DEFAULT_SERIAL_BAUD_RATE); + this->add_module( this->serial ); + } + if (SMOOTHIE_UART_SECONDARY_ENABLE) { + this->secondary_serial = new SerialConsole(SMOOTHIE_UART_SECONDARY_TX, SMOOTHIE_UART_SECONDARY_RX, DEFAULT_SERIAL_BAUD_RATE); + this->add_module( this->secondary_serial ); + } + // Config next, but does not load cache yet this->config = new Config(); // Pre-load the config cache, do after setting up serial so we can report errors to serial this->config->config_cache_load(); - // ADC reading - this->adc = new Adc(); + // now config is loaded we can do normal setup for serial based on config + delete this->serial; + this->serial= NULL; - // For slow repeteative tasks - this->add_module( this->slow_ticker = new SlowTicker()); + this->streams = new StreamOutputPool(); this->current_path = "/"; + // Configure UART depending on MRI config + // Match up the SerialConsole to MRI UART. This makes it easy to use only one UART for both debug and actual commands. + NVIC_SetPriorityGrouping(0); + +#if MRI_ENABLE != 0 + switch( __mriPlatform_CommUartIndex() ) { + case 0: + this->serial = new (SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + case 1: + this->serial = new SerialConsole( p13, p14, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + case 2: + this->serial = new SerialConsole( p28, p27, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + case 3: + this->serial = new SerialConsole( p9, p10, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + break; + } +#endif + // default + if(this->serial == nullptr) { + this->serial = new SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(DEFAULT_SERIAL_BAUD_RATE)->as_number()); + } + //some boards don't have leds.. TOO BAD! this->use_leds= !this->config->value( disable_leds_checksum )->by_default(false)->as_bool(); @@ -98,23 +130,56 @@ Kernel::Kernel(){ // we exepct ok per line now not per G code, setting this to false will return to the old (incorrect) way of ok per G code this->ok_per_line= this->config->value( ok_per_line_checksum )->by_default(true)->as_bool(); - // Configure the step ticker - this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); -// float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); - //this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(1000)->as_number(); + this->add_module( this->serial ); + // HAL stuff + add_module( this->slow_ticker = new SlowTicker()); this->step_ticker = new StepTicker(); + this->adc = new Adc(); + + // TODO : These should go into platform-specific files + // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 +/* + NVIC_SetPriorityGrouping(0); + NVIC_SetPriority(TIMER0_IRQn, 2); + NVIC_SetPriority(TIMER1_IRQn, 1); + NVIC_SetPriority(TIMER2_IRQn, 4); + NVIC_SetPriority(PendSV_IRQn, 3); + + // Set other priorities lower than the timers + NVIC_SetPriority(ADC_IRQn, 5); + NVIC_SetPriority(USB_IRQn, 5); + + // If MRI is enabled + if( MRI_ENABLE ){ + if( NVIC_GetPriority(UART0_IRQn) > 0 ){ NVIC_SetPriority(UART0_IRQn, 5); } + if( NVIC_GetPriority(UART1_IRQn) > 0 ){ NVIC_SetPriority(UART1_IRQn, 5); } + if( NVIC_GetPriority(UART2_IRQn) > 0 ){ NVIC_SetPriority(UART2_IRQn, 5); } + if( NVIC_GetPriority(UART3_IRQn) > 0 ){ NVIC_SetPriority(UART3_IRQn, 5); } + }else{ + NVIC_SetPriority(UART0_IRQn, 5); + NVIC_SetPriority(UART1_IRQn, 5); + NVIC_SetPriority(UART2_IRQn, 5); + NVIC_SetPriority(UART3_IRQn, 5); + } +*/ + // Configure the step ticker + this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); + float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(1)->as_number(); + + // Configure the step ticker + this->step_ticker->set_frequency( this->base_stepping_frequency ); + this->step_ticker->set_unstep_time( microseconds_per_step_pulse ); // Core modules this->add_module( this->conveyor = new Conveyor() ); this->add_module( this->gcode_dispatch = new GcodeDispatch() ); this->add_module( this->robot = new Robot() ); - //this->add_module( this->simpleshell = new SimpleShell() ); + // TOADDBACK this->add_module( this->simpleshell = new SimpleShell() ); this->planner = new Planner(); - - // TOADDBACK this->configurator = new Configurator(); + this->configurator = new Configurator(); } // return a GRBL-like query string for serial ? @@ -196,7 +261,7 @@ void Kernel::call_event(_EVENT_ENUM id_event, void * argument){ bool was_idle= true; if(id_event == ON_HALT) { this->halted= (argument == nullptr); - was_idle= conveyor->is_idle(); // see if we were doing anything like printing + was_idle= conveyor->is_queue_empty(); // see if we were doing anything like printing } // send to all registered modules diff --git a/src/libs/Kernel.h b/src/libs/Kernel.h index 790bdfed..84934301 100644 --- a/src/libs/Kernel.h +++ b/src/libs/Kernel.h @@ -30,9 +30,8 @@ class Planner; class StepTicker; class Adc; class PublicData; -//TOADDBACKĀ class SimpleShell; -//TOADDBACK class Configurator; -using namespace std; +//TOADDBACK class SimpleShell; +class Configurator; class Kernel { public: @@ -66,8 +65,8 @@ class Kernel { Planner* planner; Config* config; Conveyor* conveyor; - // TOADDBACKĀ Configurator* configurator; - // TOADDBACKĀ SimpleShell* simpleshell; + Configurator* configurator; + // TOADDBACK SimpleShell* simpleshell; int debug; SlowTicker* slow_ticker; @@ -86,7 +85,6 @@ class Kernel { bool feed_hold:1; bool ok_per_line:1; }; - }; #endif diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 23987cfb..8d9ba401 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -19,8 +19,6 @@ //#include "system_LPC17xx.h" // mbed.h lib #include #include -#include "SEGGER_SYSVIEW.h" -#include "SEGGER_RTT_Conf.h" #ifdef STEPTICKER_DEBUG_PIN // debug pins, only used if defined in src/makefile @@ -56,7 +54,8 @@ StepTicker::StepTicker() LPC_CCU1->CLKCCU[CLK_MX_TIMER1].CFG |= 1; LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER1_RST & 31)))){} - /* Configure Timer 0 */ + + /* Configure Timer 1 */ LPC_TIMER1->CTCR = 0x0; // timer mode LPC_TIMER1->TCR = 0; // Disable interrupt LPC_TIMER1->PR = prescale - 1; @@ -65,8 +64,7 @@ StepTicker::StepTicker() // Default start values this->set_frequency(100000); - this->set_reset_delay(5); - //TODO REMOVE this->set_unstep_time(100); + this->set_unstep_time(100); this->unstep.reset(); this->num_motors = 0; @@ -81,7 +79,8 @@ StepTicker::StepTicker() #endif } -StepTicker::~StepTicker() { +StepTicker::~StepTicker() +{ } //called when everything is setup and interrupts can start @@ -100,12 +99,12 @@ void StepTicker::set_frequency( float frequency ) /* Update Timer 0 Match register and reset timer */ LPC_TIMER0->MR[0] = this->period_us; - LPC_TIMER0->TCR = 3; // Reset + LPC_TIMER0->TCR = 3; // Reset LPC_TIMER0->TCR = 1; // start } // Set the reset delay -void StepTicker::set_reset_delay( float microseconds ){ +void StepTicker::set_unstep_time( float microseconds ){ this->reset_delay_us = (int)lrint(microseconds); /* Update Timer 1 Match register */ LPC_TIMER1->MR[0] = this->reset_delay_us; @@ -116,10 +115,8 @@ void StepTicker::set_reset_delay( float microseconds ){ // Reset step pins on any motor that was stepped void StepTicker::unstep_tick() { - for (int i = 0; i < num_motors; i++) - { - if(this->unstep[i]) - { + for (int i = 0; i < num_motors; i++) { + if(this->unstep[i]) { this->motor[i]->unstep(); } } @@ -130,23 +127,17 @@ void StepTicker::unstep_tick() // Unstep timer interrupt handler extern "C" void TIMER1_IRQHandler (void) { -// LPC_TIMER1->IR |= 1 << 0; StepTicker::getInstance()->unstep_tick(); NVIC_ClearPendingIRQ(TIMER1_IRQn); -// SEGGER_SYSVIEW_RecordExitISR(); } -// The actual interrupt handler where we do all the work - the step_tick +// Step timer interrupt handler extern "C" void TIMER0_IRQHandler (void) { -// SEGGER_RTT_LOCK(); -// SEGGER_SYSVIEW_RecordEnterISR(); LPC_TIMER0->IR |= 1 << 0; StepTicker::getInstance()->step_tick(); NVIC_ClearPendingIRQ(TIMER0_IRQn); -// SEGGER_SYSVIEW_RecordExitISR(); -// SEGGER_RTT_UNLOCK(); } extern "C" void PendSV_Handler(void) @@ -249,6 +240,7 @@ void StepTicker::step_tick (void) LPC_TIMER1->TCR = 1; // enable = 1, reset = 0 } + // see if any motors are still moving if(!still_moving) { //SET_STEPTICKER_DEBUG_PIN(0); diff --git a/src/libs/StepTicker.h b/src/libs/StepTicker.h index f23d19f2..63e443f7 100644 --- a/src/libs/StepTicker.h +++ b/src/libs/StepTicker.h @@ -18,8 +18,6 @@ #include "ActuatorCoordinates.h" #include "TSRingBuffer.h" -#include "mbed.h" - class StepperMotor; class Block; @@ -33,8 +31,7 @@ class StepTicker{ StepTicker(); ~StepTicker(); void set_frequency( float frequency ); - void set_reset_delay( float seconds ); - //TODO REMOVE void set_unstep_time( float microseconds ); + void set_unstep_time( float microseconds ); int register_motor(StepperMotor* motor); float get_frequency() const { return frequency; } void unstep_tick(); @@ -48,15 +45,18 @@ class StepTicker{ std::function finished_fnc{nullptr}; static StepTicker *getInstance() { return instance; } - uint32_t reset_delay_us; + // TOADDBACK was private + volatile uint32_t tick_cnt; + uint32_t period_us; + uint32_t reset_delay_us; private: static StepTicker *instance; bool start_next_block(); float frequency; - uint32_t period_us; + uint32_t period; std::array motor; std::bitset unstep; diff --git a/src/libs/StepperMotor.cpp b/src/libs/StepperMotor.cpp index cdc533af..f2183cee 100644 --- a/src/libs/StepperMotor.cpp +++ b/src/libs/StepperMotor.cpp @@ -6,8 +6,7 @@ */ #include "StepperMotor.h" -#include "libs/Module.h" -#include "libs/Kernel.h" +#include "Kernel.h" //#include "MRI_Hooks.h" #include "StepTicker.h" @@ -16,6 +15,7 @@ StepperMotor::StepperMotor(Pin &step, Pin &dir, Pin &en) : step_pin(step), dir_pin(dir), en_pin(en) { + //TODO ADDBACK set_high_on_debug(en.port_number, en.pin_number); steps_per_mm = 1.0F; max_rate = 50.0F; @@ -29,7 +29,7 @@ StepperMotor::StepperMotor(Pin &step, Pin &dir, Pin &en) : step_pin(step), dir_p enable(false); unstep(); // initialize step pin - set_direction(false); // initialize direction pin + set_direction(false); // initialize dor pin this->register_for_event(ON_HALT); this->register_for_event(ON_ENABLE); @@ -94,3 +94,27 @@ int32_t StepperMotor::steps_to_target(float target) int32_t target_steps = lroundf(target * steps_per_mm); return target_steps - last_milestone_steps; } + +// Does a manual step pulse, used for direct encoder control of a stepper +// NOTE this is experimental and may change and/or be reomved in the future, it is an unsupported feature. +// use at your own risk +void StepperMotor::manual_step(bool dir) +{ + if(!is_enabled()) enable(true); + + // set direction if needed + if(this->direction != dir) { + this->direction= dir; + this->dir_pin.set(dir); + wait_us(1); + } + + // pulse step pin + this->step_pin.set(1); + wait_us(3); + this->step_pin.set(0); + + + // keep track of actuators actual position in steps + this->current_position_steps += (dir ? -1 : 1); +} diff --git a/src/libs/StepperMotor.h b/src/libs/StepperMotor.h index 74afd3e5..5b017fb7 100644 --- a/src/libs/StepperMotor.h +++ b/src/libs/StepperMotor.h @@ -9,12 +9,9 @@ #include "Module.h" #include "Pin.h" -#include -#include class StepperMotor : public Module { public: - StepperMotor(); StepperMotor(Pin& step, Pin& dir, Pin& en); ~StepperMotor(); @@ -29,7 +26,7 @@ class StepperMotor : public Module { inline void set_direction(bool f) { dir_pin.set(f); direction= f; } void enable(bool state) { en_pin.set(!state); }; - //bool is_enabled() const { return !en_pin.get(); }; + bool is_enabled() { return !en_pin.get(); }; bool is_moving() const { return moving; }; void start_moving() { moving= true; } void stop_moving() { moving= false; } @@ -57,6 +54,7 @@ class StepperMotor : public Module { int32_t steps_to_target(float); + private: void on_halt(void *argument); void on_enable(void *argument); @@ -74,17 +72,6 @@ class StepperMotor : public Module { int32_t last_milestone_steps; float last_milestone_mm; - uint32_t steps_to_move; - uint32_t stepped; - uint32_t last_step_tick; - uint32_t signal_step; - - // set to 32 bit fixed point, 18:14 bits fractional - static const uint32_t fx_shift= 14; - static const uint32_t fx_increment= ((uint32_t)1<robot->next_command_is_MCS= true; + THEROBOT->next_command_is_MCS= true; } @@ -270,7 +270,8 @@ void GcodeDispatch::on_console_line_received(void *line) delete gcode; // TODOĀ :Ā TOADDBACK When Simpleshell is added back in - /*if(str.empty()) { + /* + if(str.empty()) { SimpleShell::parse_command("help", "", new_message.stream); }else{ @@ -312,8 +313,8 @@ void GcodeDispatch::on_console_line_received(void *line) string arg= get_arguments(single_command + possible_command); // rest of line is filename if(arg.empty()) arg= "/sd/config-override"; else arg= "/sd/config-override." + arg; - new_message.stream->printf("args: <%s>\n", arg.c_str()); - //TOADDBACKĀ : SimpleShell::parse_command((gcode->m == 501) ? "load_command" : "save_command", arg, new_message.stream); + //new_message.stream->printf("args: <%s>\n", arg.c_str()); + //TODO ADDBACK SimpleShell::parse_command((gcode->m == 501) ? "load_command" : "save_command", arg, new_message.stream); } delete gcode; new_message.stream->printf("ok\r\n"); diff --git a/src/modules/communication/utils/Gcode.cpp b/src/modules/communication/utils/Gcode.cpp old mode 100644 new mode 100755 index 1de67536..e4e8cd68 --- a/src/modules/communication/utils/Gcode.cpp +++ b/src/modules/communication/utils/Gcode.cpp @@ -23,6 +23,7 @@ Gcode::Gcode(const string &command, StreamOutput *stream, bool strip) this->add_nl= false; this->is_error= false; this->stream= stream; + this->millimeters_of_travel = 0.0F; prepare_cached_values(strip); this->stripped= strip; } @@ -38,6 +39,7 @@ Gcode::~Gcode() Gcode::Gcode(const Gcode &to_copy) { this->command = strdup(to_copy.command); // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store + this->millimeters_of_travel = to_copy.millimeters_of_travel; this->has_m = to_copy.has_m; this->has_g = to_copy.has_g; this->m = to_copy.m; @@ -53,6 +55,7 @@ Gcode &Gcode::operator= (const Gcode &to_copy) { if( this != &to_copy ) { this->command = strdup(to_copy.command); // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store + this->millimeters_of_travel = to_copy.millimeters_of_travel; this->has_m = to_copy.has_m; this->has_g = to_copy.has_g; this->m = to_copy.m; @@ -175,12 +178,15 @@ void Gcode::prepare_cached_values(bool strip) if( this->has_letter('G') ) { this->has_g = true; this->g = this->get_int('G', &p); + } else { this->has_g = false; } + if( this->has_letter('M') ) { this->has_m = true; this->m = this->get_int('M', &p); + } else { this->has_m = false; } diff --git a/src/modules/communication/utils/Gcode.h b/src/modules/communication/utils/Gcode.h old mode 100644 new mode 100755 index ad124e2e..aa718f91 --- a/src/modules/communication/utils/Gcode.h +++ b/src/modules/communication/utils/Gcode.h @@ -36,6 +36,7 @@ class Gcode { // FIXME these should be private unsigned int m; unsigned int g; + float millimeters_of_travel; struct { bool add_nl:1; diff --git a/src/modules/robot/ActuatorCoordinates.h b/src/modules/robot/ActuatorCoordinates.h old mode 100644 new mode 100755 index 78763fd5..03c0e82b --- a/src/modules/robot/ActuatorCoordinates.h +++ b/src/modules/robot/ActuatorCoordinates.h @@ -10,10 +10,14 @@ #include #ifndef MAX_ROBOT_ACTUATORS -#define MAX_ROBOT_ACTUATORS 3 + #ifdef CNC + #define MAX_ROBOT_ACTUATORS 3 + #else + // includes 2 extruders + #define MAX_ROBOT_ACTUATORS 5 + #endif #endif -//The subset in use is determined by the arm solution's get_actuator_count(). -//Keep MAX_ROBOT_ACTUATORS as small as practical it impacts block size and therefore free memory. +// Keep MAX_ROBOT_ACTUATORS as small as practical it impacts block size and therefore free memory. const size_t k_max_actuators = MAX_ROBOT_ACTUATORS; typedef struct std::array ActuatorCoordinates; diff --git a/src/modules/robot/Block.cpp b/src/modules/robot/Block.cpp old mode 100644 new mode 100755 diff --git a/src/modules/robot/Block.h b/src/modules/robot/Block.h old mode 100644 new mode 100755 diff --git a/src/modules/robot/Robot.cpp b/src/modules/robot/Robot.cpp old mode 100644 new mode 100755 index e9512ba1..dc6eea6b --- a/src/modules/robot/Robot.cpp +++ b/src/modules/robot/Robot.cpp @@ -1001,7 +1001,6 @@ bool Robot::append_milestone(const float target[], float rate_mm_s) // as the last milestone won't be updated we do not actually lose any moves as they will be accounted for in the next move if(!auxilliary_move && distance < 0.00001F) return false; - if(!auxilliary_move) { for (size_t i = X_AXIS; i <= Z_AXIS; i++) { // find distance unit vector for primary axis only diff --git a/src/modules/robot/Robot.h b/src/modules/robot/Robot.h old mode 100644 new mode 100755 index 51e61f81..264fc677 --- a/src/modules/robot/Robot.h +++ b/src/modules/robot/Robot.h @@ -18,8 +18,6 @@ using std::string; #include "libs/Module.h" #include "ActuatorCoordinates.h" #include "nuts_bolts.h" -#include "Pin.h" -#include class Gcode; class BaseSolution; @@ -147,19 +145,8 @@ class Robot : public Module { uint8_t selected_extruder; uint8_t n_motors; //count of the motors/axis registered - //used to initialize the TMC2130 stepper motor driver if present - mbed::SPI *spi; - Pin spi_sck_pin; - Pin spi_mosi_pin; - Pin spi_miso_pin; - //Note the spi_cs pin is read per stepper from the config file, for example for a Bamabino210[E] board: - //alpha_spi_cs_pin 7.4 # Pin for alpha SilentStepStick SPI Enable pin - //beta_spi_cs_pin 7.5 # Pin for beta SilentStepStick SPI Enable pin - //gamma_spi_cs_pin 4.1 # Pin for gamma SilentStepStick SPI Enable pin - - // Used by Stepper, Planner + // Used by Planner friend class Planner; - friend class Stepper; }; diff --git a/src/modules/robot/arm_solutions/BaseSolution.h b/src/modules/robot/arm_solutions/BaseSolution.h old mode 100644 new mode 100755 index 76196c01..92b2c811 --- a/src/modules/robot/arm_solutions/BaseSolution.h +++ b/src/modules/robot/arm_solutions/BaseSolution.h @@ -12,11 +12,12 @@ class BaseSolution { BaseSolution(){}; BaseSolution(Config*){}; virtual ~BaseSolution() {}; - virtual void cartesian_to_actuator(const float[], ActuatorCoordinates &) const = 0; - virtual void actuator_to_cartesian(const ActuatorCoordinates &, float[]) const = 0; + virtual void cartesian_to_actuator(const float[], ActuatorCoordinates &) = 0; + virtual void actuator_to_cartesian(const ActuatorCoordinates &, float[]) = 0; typedef std::map arm_options_t; virtual bool set_optional(const arm_options_t& options) { return false; }; - virtual bool get_optional(arm_options_t& options, bool force_all= false) const { return false; }; + virtual bool get_optional(arm_options_t& options, bool force_all= false) { return false; }; + virtual size_t get_actuator_count() const { return 3; } }; #endif diff --git a/src/modules/robot/arm_solutions/CartesianSolution.cpp b/src/modules/robot/arm_solutions/CartesianSolution.cpp old mode 100644 new mode 100755 index 73fd4c51..df2d901d --- a/src/modules/robot/arm_solutions/CartesianSolution.cpp +++ b/src/modules/robot/arm_solutions/CartesianSolution.cpp @@ -2,13 +2,13 @@ #include "ActuatorCoordinates.h" #include -void CartesianSolution::cartesian_to_actuator( const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const { +void CartesianSolution::cartesian_to_actuator( const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ actuator_mm[ALPHA_STEPPER] = cartesian_mm[X_AXIS]; actuator_mm[BETA_STEPPER ] = cartesian_mm[Y_AXIS]; actuator_mm[GAMMA_STEPPER] = cartesian_mm[Z_AXIS]; } -void CartesianSolution::actuator_to_cartesian( const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const { +void CartesianSolution::actuator_to_cartesian( const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ cartesian_mm[ALPHA_STEPPER] = actuator_mm[X_AXIS]; cartesian_mm[BETA_STEPPER ] = actuator_mm[Y_AXIS]; cartesian_mm[GAMMA_STEPPER] = actuator_mm[Z_AXIS]; diff --git a/src/modules/robot/arm_solutions/CartesianSolution.h b/src/modules/robot/arm_solutions/CartesianSolution.h old mode 100644 new mode 100755 index f1a84613..847f6a69 --- a/src/modules/robot/arm_solutions/CartesianSolution.h +++ b/src/modules/robot/arm_solutions/CartesianSolution.h @@ -1,5 +1,5 @@ -#pragma once - +#ifndef CARTESIANSOLUTION_H +#define CARTESIANSOLUTION_H #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -11,6 +11,13 @@ class CartesianSolution : public BaseSolution { public: CartesianSolution(){}; CartesianSolution(Config*){}; - void cartesian_to_actuator( const float millimeters[], ActuatorCoordinates &steps ) const override; - void actuator_to_cartesian( const ActuatorCoordinates &steps, float millimeters[] ) const override; + void cartesian_to_actuator( const float millimeters[], ActuatorCoordinates &steps ) override; + void actuator_to_cartesian( const ActuatorCoordinates &steps, float millimeters[] ) override; }; + + + + + + +#endif diff --git a/src/modules/robot/arm_solutions/CoreXZSolution.cpp b/src/modules/robot/arm_solutions/CoreXZSolution.cpp old mode 100644 new mode 100755 index 4282e1ac..01003f7e --- a/src/modules/robot/arm_solutions/CoreXZSolution.cpp +++ b/src/modules/robot/arm_solutions/CoreXZSolution.cpp @@ -12,13 +12,13 @@ CoreXZSolution::CoreXZSolution(Config* config) z_reduction = config->value(z_reduction_checksum)->by_default(3.0f)->as_number(); } -void CoreXZSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const { +void CoreXZSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ actuator_mm[ALPHA_STEPPER] = (this->x_reduction * cartesian_mm[X_AXIS]) + (this->z_reduction * cartesian_mm[Z_AXIS]); actuator_mm[BETA_STEPPER ] = (this->x_reduction * cartesian_mm[X_AXIS]) - (this->z_reduction * cartesian_mm[Z_AXIS]); actuator_mm[GAMMA_STEPPER] = cartesian_mm[Y_AXIS]; } -void CoreXZSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const { +void CoreXZSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ cartesian_mm[X_AXIS] = (0.5F/this->x_reduction) * (actuator_mm[ALPHA_STEPPER] + actuator_mm[BETA_STEPPER]); cartesian_mm[Z_AXIS] = (0.5F/this->z_reduction) * (actuator_mm[ALPHA_STEPPER] - actuator_mm[BETA_STEPPER]); cartesian_mm[Y_AXIS] = actuator_mm[GAMMA_STEPPER]; diff --git a/src/modules/robot/arm_solutions/CoreXZSolution.h b/src/modules/robot/arm_solutions/CoreXZSolution.h old mode 100644 new mode 100755 index 4b3d7995..bca314f3 --- a/src/modules/robot/arm_solutions/CoreXZSolution.h +++ b/src/modules/robot/arm_solutions/CoreXZSolution.h @@ -1,5 +1,5 @@ -#pragma once - +#ifndef COREXZSOLUTION_H +#define COREXZSOLUTION_H #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -10,10 +10,12 @@ class CoreXZSolution : public BaseSolution { public: CoreXZSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates & ) const override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; + void cartesian_to_actuator(const float[], ActuatorCoordinates & ) override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; private: float x_reduction; float z_reduction; }; + +#endif // COREXZSOLUTION_H diff --git a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp old mode 100644 new mode 100755 index 2d7aed77..69ad4ea3 --- a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.cpp @@ -38,15 +38,14 @@ ExperimentalDeltaSolution::ExperimentalDeltaSolution(Config* config) arm_length_squared = powf(arm_length, 2); } -void ExperimentalDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const -{ +void ExperimentalDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ float alpha_rotated[3], rotated[3]; - if( sin_alpha == 0 && cos_alpha == 1) { + if( sin_alpha == 0 && cos_alpha == 1){ alpha_rotated[X_AXIS] = cartesian_mm[X_AXIS]; alpha_rotated[Y_AXIS] = cartesian_mm[Y_AXIS]; alpha_rotated[Z_AXIS] = cartesian_mm[Z_AXIS]; - } else { + }else{ rotate( cartesian_mm, alpha_rotated, sin_alpha, cos_alpha ); } actuator_mm[ALPHA_STEPPER] = solve_arm( alpha_rotated ); @@ -58,18 +57,15 @@ void ExperimentalDeltaSolution::cartesian_to_actuator(const float cartesian_mm[] actuator_mm[GAMMA_STEPPER] = solve_arm( rotated ); } -void ExperimentalDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const -{ +void ExperimentalDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ // unimplemented } -float ExperimentalDeltaSolution::solve_arm( float cartesian_mm[]) const -{ +float ExperimentalDeltaSolution::solve_arm( float cartesian_mm[]) { return sqrtf(arm_length_squared - powf(cartesian_mm[X_AXIS] - arm_radius, 2) - powf(cartesian_mm[Y_AXIS], 2)) + cartesian_mm[Z_AXIS]; } -void ExperimentalDeltaSolution::rotate(const float in[], float out[], float sin, float cos ) const -{ +void ExperimentalDeltaSolution::rotate(const float in[], float out[], float sin, float cos ){ out[X_AXIS] = cos * in[X_AXIS] - sin * in[Y_AXIS]; out[Y_AXIS] = sin * in[X_AXIS] + cos * in[Y_AXIS]; out[Z_AXIS] = in[Z_AXIS]; diff --git a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h old mode 100644 new mode 100755 index 16d38a86..c888c3d3 --- a/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h +++ b/src/modules/robot/arm_solutions/ExperimentalDeltaSolution.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef EXPERIMENTALDELTASOLUTION_H +#define EXPERIMENTALDELTASOLUTION_H #include "BaseSolution.h" @@ -7,12 +8,12 @@ class Config; class ExperimentalDeltaSolution : public BaseSolution { public: ExperimentalDeltaSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; private: - float solve_arm( float millimeters[] ) const ; - void rotate(const float in[], float out[], float sin, float cos ) const ; + float solve_arm( float millimeters[] ); + void rotate(const float in[], float out[], float sin, float cos ); float arm_length; float arm_radius; @@ -25,3 +26,10 @@ class ExperimentalDeltaSolution : public BaseSolution { float sin_gamma; float cos_gamma; }; + + + + + + +#endif // EXPERIMENTALDELTASOLUTION_H diff --git a/src/modules/robot/arm_solutions/HBotSolution.cpp b/src/modules/robot/arm_solutions/HBotSolution.cpp old mode 100644 new mode 100755 index 3a6655a7..70f56458 --- a/src/modules/robot/arm_solutions/HBotSolution.cpp +++ b/src/modules/robot/arm_solutions/HBotSolution.cpp @@ -2,15 +2,13 @@ #include "ActuatorCoordinates.h" #include -void HBotSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const -{ +void HBotSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ actuator_mm[ALPHA_STEPPER] = cartesian_mm[X_AXIS] + cartesian_mm[Y_AXIS]; actuator_mm[BETA_STEPPER ] = cartesian_mm[X_AXIS] - cartesian_mm[Y_AXIS]; actuator_mm[GAMMA_STEPPER] = cartesian_mm[Z_AXIS]; } -void HBotSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const -{ +void HBotSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ cartesian_mm[X_AXIS] = 0.5F * (actuator_mm[ALPHA_STEPPER] + actuator_mm[BETA_STEPPER]); cartesian_mm[Y_AXIS] = 0.5F * (actuator_mm[ALPHA_STEPPER] - actuator_mm[BETA_STEPPER]); cartesian_mm[Z_AXIS] = actuator_mm[GAMMA_STEPPER]; diff --git a/src/modules/robot/arm_solutions/HBotSolution.h b/src/modules/robot/arm_solutions/HBotSolution.h old mode 100644 new mode 100755 index 6b4281d6..c610f992 --- a/src/modules/robot/arm_solutions/HBotSolution.h +++ b/src/modules/robot/arm_solutions/HBotSolution.h @@ -1,5 +1,5 @@ -#pragma once - +#ifndef HBOTSOLUTION_H +#define HBOTSOLUTION_H #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -11,6 +11,14 @@ class HBotSolution : public BaseSolution { public: HBotSolution(); HBotSolution(Config*){}; - void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[]) const override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[]) override; }; + + + + + + +#endif // HBOTSOLUTION_H + diff --git a/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp b/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp old mode 100644 new mode 100755 index b0a16394..ad12c015 --- a/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/LinearDeltaSolution.cpp @@ -41,11 +41,10 @@ LinearDeltaSolution::LinearDeltaSolution(Config* config) init(); } -void LinearDeltaSolution::init() -{ +void LinearDeltaSolution::init() { arm_length_squared = SQ(arm_length); - // Effective X/Y positions of the three vertical towers. + // Effective X/Y positions of the three vertical towers. float delta_radius = arm_radius; delta_tower1_x = (delta_radius + tower1_offset) * cosf((210.0F + tower1_angle) * PIOVER180); // front left tower @@ -56,7 +55,7 @@ void LinearDeltaSolution::init() delta_tower3_y = (delta_radius + tower3_offset) * sinf((90.0F + tower3_angle) * PIOVER180); } -void LinearDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const +void LinearDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) { actuator_mm[ALPHA_STEPPER] = sqrtf(this->arm_length_squared @@ -73,7 +72,7 @@ void LinearDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu ) + cartesian_mm[Z_AXIS]; } -void LinearDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const +void LinearDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) { // from http://en.wikipedia.org/wiki/Circumscribed_circle#Barycentric_coordinates_from_cross-_and_dot-products // based on https://github.com/ambrop72/aprinter/blob/2de69a/aprinter/printer/DeltaTransform.h#L81 @@ -112,13 +111,12 @@ void LinearDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actua cartesian_mm[2] = ROUND(cartesian[2], 4); } -bool LinearDeltaSolution::set_optional(const arm_options_t& options) -{ +bool LinearDeltaSolution::set_optional(const arm_options_t& options) { for(auto &i : options) { switch(i.first) { - case 'L': arm_length = i.second; break; - case 'R': arm_radius = i.second; break; + case 'L': arm_length= i.second; break; + case 'R': arm_radius= i.second; break; case 'A': tower1_offset = i.second; break; case 'B': tower2_offset = i.second; break; case 'C': tower3_offset = i.second; break; @@ -132,14 +130,13 @@ bool LinearDeltaSolution::set_optional(const arm_options_t& options) return true; } -bool LinearDeltaSolution::get_optional(arm_options_t& options, bool force_all) const -{ - options['L'] = this->arm_length; - options['R'] = this->arm_radius; +bool LinearDeltaSolution::get_optional(arm_options_t& options, bool force_all) { + options['L']= this->arm_length; + options['R']= this->arm_radius; // don't report these if none of them are set if(force_all || (this->tower1_offset != 0.0F || this->tower2_offset != 0.0F || this->tower3_offset != 0.0F || - this->tower1_angle != 0.0F || this->tower2_angle != 0.0F || this->tower3_angle != 0.0F) ) { + this->tower1_angle != 0.0F || this->tower2_angle != 0.0F || this->tower3_angle != 0.0F) ) { options['A'] = this->tower1_offset; options['B'] = this->tower2_offset; diff --git a/src/modules/robot/arm_solutions/LinearDeltaSolution.h b/src/modules/robot/arm_solutions/LinearDeltaSolution.h old mode 100644 new mode 100755 index 401b7189..b7ebd208 --- a/src/modules/robot/arm_solutions/LinearDeltaSolution.h +++ b/src/modules/robot/arm_solutions/LinearDeltaSolution.h @@ -1,5 +1,5 @@ -#pragma once - +#ifndef LINEARDELTASOLUTION_H +#define LINEARDELTASOLUTION_H #include "libs/Module.h" #include "BaseSolution.h" @@ -8,11 +8,11 @@ class Config; class LinearDeltaSolution : public BaseSolution { public: LinearDeltaSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; bool set_optional(const arm_options_t& options) override; - bool get_optional(arm_options_t& options, bool force_all) const override; + bool get_optional(arm_options_t& options, bool force_all) override; private: void init(); @@ -34,3 +34,4 @@ class LinearDeltaSolution : public BaseSolution { float tower2_angle; float tower3_angle; }; +#endif // LINEARDELTASOLUTION_H diff --git a/src/modules/robot/arm_solutions/MorganSCARASolution.cpp b/src/modules/robot/arm_solutions/MorganSCARASolution.cpp old mode 100644 new mode 100755 index dee86c4d..304da817 --- a/src/modules/robot/arm_solutions/MorganSCARASolution.cpp +++ b/src/modules/robot/arm_solutions/MorganSCARASolution.cpp @@ -51,17 +51,15 @@ MorganSCARASolution::MorganSCARASolution(Config* config) init(); } -void MorganSCARASolution::init() -{ +void MorganSCARASolution::init() { } -float MorganSCARASolution::to_degrees(float radians) const -{ - return radians * (180.0F / 3.14159265359f); +float MorganSCARASolution::to_degrees(float radians) { + return radians*(180.0F/3.14159265359f); } -void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const +void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) { float SCARA_pos[2], @@ -77,9 +75,9 @@ void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], Actu // Y has to be scaled before subtracting offset to ensure position on bed. if (this->arm1_length == this->arm2_length) - SCARA_C2 = (SQ(SCARA_pos[X_AXIS]) + SQ(SCARA_pos[Y_AXIS]) - 2.0f * SQ(this->arm1_length)) / (2.0f * SQ(this->arm1_length)); + SCARA_C2 = (SQ(SCARA_pos[X_AXIS])+SQ(SCARA_pos[Y_AXIS])-2.0f*SQ(this->arm1_length)) / (2.0f * SQ(this->arm1_length)); else - SCARA_C2 = (SQ(SCARA_pos[X_AXIS]) + SQ(SCARA_pos[Y_AXIS]) - SQ(this->arm1_length) - SQ(this->arm2_length)) / (2.0f * SQ(this->arm1_length)); + SCARA_C2 = (SQ(SCARA_pos[X_AXIS])+SQ(SCARA_pos[Y_AXIS])-SQ(this->arm1_length)-SQ(this->arm2_length)) / (2.0f * SQ(this->arm1_length)); // SCARA position is undefined if abs(SCARA_C2) >=1 // In reality abs(SCARA_C2) >0.95 can be problematic. @@ -90,13 +88,13 @@ void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], Actu SCARA_C2 = -this->morgan_undefined_min; - SCARA_S2 = sqrtf(1.0f - SQ(SCARA_C2)); + SCARA_S2 = sqrtf(1.0f-SQ(SCARA_C2)); - SCARA_K1 = this->arm1_length + this->arm2_length * SCARA_C2; - SCARA_K2 = this->arm2_length * SCARA_S2; + SCARA_K1 = this->arm1_length+this->arm2_length*SCARA_C2; + SCARA_K2 = this->arm2_length*SCARA_S2; - SCARA_theta = (atan2f(SCARA_pos[X_AXIS], SCARA_pos[Y_AXIS]) - atan2f(SCARA_K1, SCARA_K2)) * -1.0f; // Morgan Thomas turns Theta in oposite direction - SCARA_psi = atan2f(SCARA_S2, SCARA_C2); + SCARA_theta = (atan2f(SCARA_pos[X_AXIS],SCARA_pos[Y_AXIS])-atan2f(SCARA_K1, SCARA_K2))*-1.0f; // Morgan Thomas turns Theta in oposite direction + SCARA_psi = atan2f(SCARA_S2,SCARA_C2); actuator_mm[ALPHA_STEPPER] = to_degrees(SCARA_theta); // Multiply by 180/Pi - theta is support arm angle @@ -106,20 +104,19 @@ void MorganSCARASolution::cartesian_to_actuator(const float cartesian_mm[], Actu } -void MorganSCARASolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const -{ +void MorganSCARASolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) { // Perform forward kinematics, and place results in cartesian_mm[] float y1, y2, - actuator_rad[2]; + actuator_rad[2]; - actuator_rad[X_AXIS] = actuator_mm[X_AXIS] / (180.0F / 3.14159265359f); - actuator_rad[Y_AXIS] = actuator_mm[Y_AXIS] / (180.0F / 3.14159265359f); + actuator_rad[X_AXIS] = actuator_mm[X_AXIS]/(180.0F/3.14159265359f); + actuator_rad[Y_AXIS] = actuator_mm[Y_AXIS]/(180.0F/3.14159265359f); - y1 = sinf(actuator_rad[X_AXIS]) * this->arm1_length; - y2 = sinf(actuator_rad[Y_AXIS]) * this->arm2_length + y1; + y1 = sinf(actuator_rad[X_AXIS])*this->arm1_length; + y2 = sinf(actuator_rad[Y_AXIS])*this->arm2_length + y1; - cartesian_mm[X_AXIS] = (((cosf(actuator_rad[X_AXIS]) * this->arm1_length) + (cosf(actuator_rad[Y_AXIS]) * this->arm2_length)) / this->morgan_scaling_x) + this->morgan_offset_x; + cartesian_mm[X_AXIS] = (((cosf(actuator_rad[X_AXIS])*this->arm1_length) + (cosf(actuator_rad[Y_AXIS])*this->arm2_length)) / this->morgan_scaling_x) + this->morgan_offset_x; cartesian_mm[Y_AXIS] = (y2 + this->morgan_offset_y) / this->morgan_scaling_y; cartesian_mm[Z_AXIS] = actuator_mm[Z_AXIS]; @@ -128,45 +125,44 @@ void MorganSCARASolution::actuator_to_cartesian(const ActuatorCoordinates &actua cartesian_mm[2] = ROUND(cartesian_mm[2], 7); } -bool MorganSCARASolution::set_optional(const arm_options_t& options) -{ +bool MorganSCARASolution::set_optional(const arm_options_t& options) { arm_options_t::const_iterator i; - i = options.find('T'); // Theta arm1 length + i= options.find('T'); // Theta arm1 length if(i != options.end()) { - arm1_length = i->second; + arm1_length= i->second; } - i = options.find('P'); // Psi arm2 length + i= options.find('P'); // Psi arm2 length if(i != options.end()) { - arm2_length = i->second; + arm2_length= i->second; } - i = options.find('X'); // Home initial position X + i= options.find('X'); // Home initial position X if(i != options.end()) { - morgan_offset_x = i->second; + morgan_offset_x= i->second; } - i = options.find('Y'); // Home initial position Y + i= options.find('Y'); // Home initial position Y if(i != options.end()) { - morgan_offset_y = i->second; + morgan_offset_y= i->second; } - i = options.find('A'); // Scaling X_AXIS + i= options.find('A'); // Scaling X_AXIS if(i != options.end()) { - morgan_scaling_x = i->second; + morgan_scaling_x= i->second; } - i = options.find('B'); // Scaling Y_AXIS + i= options.find('B'); // Scaling Y_AXIS if(i != options.end()) { - morgan_scaling_y = i->second; + morgan_scaling_y= i->second; } //i= options.find('C'); // Scaling Z_AXIS //if(i != options.end()) { // morgan_scaling_z= i->second; //} - i = options.find('D'); // Undefined min + i= options.find('D'); // Undefined min if(i != options.end()) { this->morgan_undefined_min = i->second; } - i = options.find('E'); // undefined max + i= options.find('E'); // undefined max if(i != options.end()) { this->morgan_undefined_max = i->second; } @@ -175,17 +171,16 @@ bool MorganSCARASolution::set_optional(const arm_options_t& options) return true; } -bool MorganSCARASolution::get_optional(arm_options_t& options, bool force_all) const -{ - options['T'] = this->arm1_length; - options['P'] = this->arm2_length; - options['X'] = this->morgan_offset_x; - options['Y'] = this->morgan_offset_y; - options['A'] = this->morgan_scaling_x; - options['B'] = this->morgan_scaling_y; +bool MorganSCARASolution::get_optional(arm_options_t& options, bool force_all) { + options['T']= this->arm1_length; + options['P']= this->arm2_length; + options['X']= this->morgan_offset_x; + options['Y']= this->morgan_offset_y; + options['A']= this->morgan_scaling_x; + options['B']= this->morgan_scaling_y; // options['C']= this->morgan_scaling_z; - options['D'] = this->morgan_undefined_min; - options['E'] = this->morgan_undefined_max; + options['D']= this->morgan_undefined_min; + options['E']= this->morgan_undefined_max; return true; }; diff --git a/src/modules/robot/arm_solutions/MorganSCARASolution.h b/src/modules/robot/arm_solutions/MorganSCARASolution.h old mode 100644 new mode 100755 index 1ab897aa..4606c234 --- a/src/modules/robot/arm_solutions/MorganSCARASolution.h +++ b/src/modules/robot/arm_solutions/MorganSCARASolution.h @@ -8,15 +8,15 @@ class Config; class MorganSCARASolution : public BaseSolution { public: MorganSCARASolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; bool set_optional(const arm_options_t& options) override; - bool get_optional(arm_options_t& options, bool force_all) const override; + bool get_optional(arm_options_t& options, bool force_all) override; private: void init(); - float to_degrees(float radians) const; + float to_degrees(float radians); float arm1_length; float arm2_length; diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp old mode 100644 new mode 100755 index c7af9131..07aa6074 --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp @@ -62,7 +62,7 @@ RotaryDeltaSolution::RotaryDeltaSolution(Config *config) // inverse kinematics // helper functions, calculates angle theta1 (for YZ-pane) -int RotaryDeltaSolution::delta_calcAngleYZ(float x0, float y0, float z0, float &theta) const +int RotaryDeltaSolution::delta_calcAngleYZ(float x0, float y0, float z0, float &theta) { float y1 = -0.5F * tan30 * delta_f; // f/2 * tan 30 y0 -= 0.5F * tan30 * delta_e; // shift center to edge @@ -82,7 +82,7 @@ int RotaryDeltaSolution::delta_calcAngleYZ(float x0, float y0, float z0, float & // forward kinematics: (theta1, theta2, theta3) -> (x0, y0, z0) // returned status: 0=OK, -1=non-existing position -int RotaryDeltaSolution::delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0) const +int RotaryDeltaSolution::delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0) { float t = (delta_f - delta_e) * tan30 / 2.0F; float degrees_to_radians = pi / 180.0F; @@ -140,7 +140,7 @@ void RotaryDeltaSolution::init() z_calc_offset = -(delta_z_offset - tool_offset - delta_ee_offs); } -void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const +void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) { //We need to translate the Cartesian coordinates in mm to the actuator position required in mm so the stepper motor functions float alpha_theta = 0.0F; @@ -196,7 +196,7 @@ void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu } -void RotaryDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const +void RotaryDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) { float x, y, z; //Use forward kinematics @@ -231,7 +231,7 @@ bool RotaryDeltaSolution::set_optional(const arm_options_t &options) return true; } -bool RotaryDeltaSolution::get_optional(arm_options_t &options, bool force_all) const +bool RotaryDeltaSolution::get_optional(arm_options_t &options, bool force_all) { options['A'] = this->delta_e; options['B'] = this->delta_f; diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.h b/src/modules/robot/arm_solutions/RotaryDeltaSolution.h old mode 100644 new mode 100755 index ecb9e4c0..52334fab --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.h +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.h @@ -1,5 +1,5 @@ -#pragma once - +#ifndef RotaryDeltaSolution_H +#define RotaryDeltaSolution_H #include "libs/Module.h" #include "BaseSolution.h" @@ -8,16 +8,16 @@ class Config; class RotaryDeltaSolution : public BaseSolution { public: RotaryDeltaSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; bool set_optional(const arm_options_t& options) override; - bool get_optional(arm_options_t& options, bool force_all) const override; + bool get_optional(arm_options_t& options, bool force_all) override; private: void init(); - int delta_calcAngleYZ(float x0, float y0, float z0, float &theta) const; - int delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0) const; + int delta_calcAngleYZ(float x0, float y0, float z0, float &theta); + int delta_calcForward(float theta1, float theta2, float theta3, float &x0, float &y0, float &z0); float delta_e; // End effector length float delta_f; // Base length @@ -35,3 +35,4 @@ class RotaryDeltaSolution : public BaseSolution { bool mirror_xy:1; }; }; +#endif // RotaryDeltaSolution_H diff --git a/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp b/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp old mode 100644 new mode 100755 index 8797a2fc..097e3fdc --- a/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp +++ b/src/modules/robot/arm_solutions/RotatableCartesianSolution.cpp @@ -7,25 +7,21 @@ // degrees * (pi / 180) = radians #define DEG2RAD 0.01745329251994329576923690768489 -RotatableCartesianSolution::RotatableCartesianSolution(Config* config) -{ +RotatableCartesianSolution::RotatableCartesianSolution(Config* config) { float alpha_angle = config->value(alpha_angle_checksum)->by_default(0.0f)->as_number() * DEG2RAD; sin_alpha = sinf(alpha_angle); cos_alpha = cosf(alpha_angle); } -void RotatableCartesianSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ) const -{ +void RotatableCartesianSolution::cartesian_to_actuator(const float cartesian_mm[], ActuatorCoordinates &actuator_mm ){ rotate( cartesian_mm, &actuator_mm[0], sin_alpha, cos_alpha ); } -void RotatableCartesianSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) const -{ +void RotatableCartesianSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ){ rotate( &actuator_mm[0], cartesian_mm, - sin_alpha, cos_alpha ); } -void RotatableCartesianSolution::rotate(const float in[], float out[], float sin, float cos ) const -{ +void RotatableCartesianSolution::rotate(const float in[], float out[], float sin, float cos ){ out[ALPHA_STEPPER] = cos * in[X_AXIS] - sin * in[Y_AXIS]; out[BETA_STEPPER ] = sin * in[X_AXIS] + cos * in[Y_AXIS]; out[GAMMA_STEPPER] = in[Z_AXIS]; diff --git a/src/modules/robot/arm_solutions/RotatableCartesianSolution.h b/src/modules/robot/arm_solutions/RotatableCartesianSolution.h old mode 100644 new mode 100755 index 3e05dab8..519affb7 --- a/src/modules/robot/arm_solutions/RotatableCartesianSolution.h +++ b/src/modules/robot/arm_solutions/RotatableCartesianSolution.h @@ -1,5 +1,5 @@ -#pragma once - +#ifndef ROTATABLECARTESIANSOLUTION_H +#define ROTATABLECARTESIANSOLUTION_H #include "libs/Module.h" #include "libs/Kernel.h" #include "BaseSolution.h" @@ -12,12 +12,16 @@ class RotatableCartesianSolution : public BaseSolution { public: RotatableCartesianSolution(Config*); - void cartesian_to_actuator(const float[], ActuatorCoordinates &) const override; - void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) const override; + void cartesian_to_actuator(const float[], ActuatorCoordinates &) override; + void actuator_to_cartesian(const ActuatorCoordinates &, float[] ) override; private: - void rotate(const float in[], float out[], float sin, float cos) const; + void rotate(const float in[], float out[], float sin, float cos); float sin_alpha; float cos_alpha; }; + + +#endif // ROTATABLECARTESIANSOLUTION_H + diff --git a/src/modules/tools/drillingcycles/Drillingcycles.cpp b/src/modules/tools/drillingcycles/Drillingcycles.cpp old mode 100644 new mode 100755 index 399e6ae2..5672a2e4 --- a/src/modules/tools/drillingcycles/Drillingcycles.cpp +++ b/src/modules/tools/drillingcycles/Drillingcycles.cpp @@ -20,6 +20,8 @@ #include /* fmod */ + + // retract modes #define RETRACT_TO_Z 0 #define RETRACT_TO_R 1 diff --git a/src/modules/tools/drillingcycles/Drillingcycles.h b/src/modules/tools/drillingcycles/Drillingcycles.h old mode 100644 new mode 100755 From 1ae07a55e8e6c1bf66631d78e2fae4c0fa212ee9 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 31 Oct 2016 08:28:29 +1300 Subject: [PATCH 26/66] modified: ../README.md modified: libs/Kernel.cpp modified: libs/StepTicker.cpp modified: makefile modified: modules/robot/Planner.cpp --- README.md | 2 ++ src/libs/Kernel.cpp | 2 +- src/libs/StepTicker.cpp | 13 +++++++++++++ src/makefile | 2 +- src/modules/robot/Planner.cpp | 2 ++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 744fe184..3be3139b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED Current status as at 2016-10-27: +* Issue: HardFault in `planner.cpp` on line 71 `block->steps[i] = labs(steps);` +* Issue: No initial serial output on `main.ccp` on line 50 `kernel->streams->printf("Smoothie2 dev\n");` * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * More code porting underway, expected completion is 2016-10-31 diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index bcfd0a55..4163181c 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -136,7 +136,7 @@ Kernel::Kernel(){ add_module( this->slow_ticker = new SlowTicker()); this->step_ticker = new StepTicker(); - this->adc = new Adc(); +//TODO ADDBACK this->adc = new Adc(); // TODO : These should go into platform-specific files // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 8d9ba401..657acae7 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -20,6 +20,9 @@ #include #include +#include "SEGGER_SYSVIEW.h" +#include "SEGGER_RTT_Conf.h" + #ifdef STEPTICKER_DEBUG_PIN // debug pins, only used if defined in src/makefile #include "gpio.h" @@ -72,6 +75,8 @@ StepTicker::StepTicker() this->running = false; this->current_block = nullptr; + this->start(); + #ifdef STEPTICKER_DEBUG_PIN // setup debug pin if defined stepticker_debug_pin.output(); @@ -127,17 +132,25 @@ void StepTicker::unstep_tick() // Unstep timer interrupt handler extern "C" void TIMER1_IRQHandler (void) { +// SEGGER_RTT_LOCK(); +// SEGGER_SYSVIEW_RecordEnterISR(); LPC_TIMER1->IR |= 1 << 0; StepTicker::getInstance()->unstep_tick(); NVIC_ClearPendingIRQ(TIMER1_IRQn); +// SEGGER_SYSVIEW_RecordExitISR(); +// SEGGER_RTT_UNLOCK(); } // Step timer interrupt handler extern "C" void TIMER0_IRQHandler (void) { +// SEGGER_RTT_LOCK(); +// SEGGER_SYSVIEW_RecordEnterISR(); LPC_TIMER0->IR |= 1 << 0; StepTicker::getInstance()->step_tick(); NVIC_ClearPendingIRQ(TIMER0_IRQn); +// SEGGER_SYSVIEW_RecordExitISR(); +// SEGGER_RTT_UNLOCK(); } extern "C" void PendSV_Handler(void) diff --git a/src/makefile b/src/makefile index fc5c6bf7..3900ec06 100644 --- a/src/makefile +++ b/src/makefile @@ -19,7 +19,7 @@ MRI_BREAK_ON_INIT ?= 1 # specific amount of space for the stack. The heap's growth will be # constrained to reserve this much space for the stack and the stack won't be # able to grow larger than this amount. -STACK_SIZE=3072 +#STACK_SIZE=3072 # Set to 1 to allow MRI debug monitor to take full control of UART0 and use it # as a dedicated debug channel. If you are using the USB based serial port for diff --git a/src/modules/robot/Planner.cpp b/src/modules/robot/Planner.cpp index 1432e44a..d4b86ed1 100644 --- a/src/modules/robot/Planner.cpp +++ b/src/modules/robot/Planner.cpp @@ -68,6 +68,8 @@ bool Planner::append_block( ActuatorCoordinates &actuator_pos, uint8_t n_motors, // find direction block->direction_bits[i] = (steps < 0) ? 1 : 0; // save actual steps in block + uint32_t a = labs(steps); + block->steps[i] = a; block->steps[i] = labs(steps); } From 9b686753f1011ba7b43eb6cf3a133fefc465fe23 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 31 Oct 2016 08:31:16 +1300 Subject: [PATCH 27/66] modified: ../README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3be3139b..479f0d7c 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,11 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED -Current status as at 2016-10-27: +Current status as at 2016-10-31: * Issue: HardFault in `planner.cpp` on line 71 `block->steps[i] = labs(steps);` * Issue: No initial serial output on `main.ccp` on line 50 `kernel->streams->printf("Smoothie2 dev\n");` * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) -* More code porting underway, expected completion is 2016-10-31 * Some major lower level things need implementation ( USB, Ethernet, SDIO ). # Compiling @@ -60,7 +59,7 @@ TODOĀ functionality to port from v1 in more detail : * modules/tools/filamentdetector : Get PWM to work * modules/tools/spindle :Ā Get PWM to work * modules/tools/temperaturecontrol : Finish porting, get the ADC to actually work, I cannot test the AD8495 temperature inputs as I don't have one of these! - * modules/tools/zprobe :Ā Port + * modules/tools/zprobe :Ā Test * modules/tools/simpleshell :Ā Port * The FPGA requires a SGPIO spi library @@ -68,7 +67,6 @@ TODOĀ functionality to port from v1 in more detail : Things that are broken during the port and should be added back as things progress ( some things might be missing here. I try to use the TOADDBACK label when commenting things so search for that too ) :Ā  -* In GcodeDispatch.cpp : Removed dependency to Pauser.h * In Switch.cpp : Commented out all of the set_low_on_debug stuff; do we still need this? * In SlowTicker.h and Hook.h : Using doubles instead of ints for intervals/frequencies and counting down. Done to accomodate mBed, but dirty costly hack * In RingBuffer.h : Removed the irq stuff From 2f4c5c08f1522748915f0af8730ff12b5f31dbcd Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 31 Oct 2016 11:18:32 +1300 Subject: [PATCH 28/66] modified: main.cpp --- src/main.cpp | 232 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 202 insertions(+), 30 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6edae83c..e64099a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,13 +1,35 @@ -/* Test which brings default HelloWorld project from mbed online compiler - to be built under GCC. - */ -#include "mbed.h" -#include "Kernel.h" -#include "SwitchPool.h" -#include "TemperatureControlPool.h" -//#include "RotaryDeltaCalibration.h" -#include "Endstops.h" -#include "Laser.h" +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie 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. + Smoothie 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 Smoothie. If not, see . +*/ + +#include "libs/Kernel.h" + +#include "modules/tools/laser/Laser.h" +#include "modules/tools/spindle/Spindle.h" +#include "modules/tools/extruder/ExtruderMaker.h" +#include "modules/tools/temperaturecontrol/TemperatureControlPool.h" +#include "modules/tools/endstops/Endstops.h" +#include "modules/tools/zprobe/ZProbe.h" +#include "modules/tools/scaracal/SCARAcal.h" +#include "RotaryDeltaCalibration.h" +#include "modules/tools/switch/SwitchPool.h" +#include "modules/tools/temperatureswitch/TemperatureSwitch.h" +#include "modules/tools/drillingcycles/Drillingcycles.h" +#include "FilamentDetector.h" +#include "MotorDriverControl.h" + +#include "modules/robot/Conveyor.h" +//#include "modules/utils/simpleshell/SimpleShell.h" +#include "modules/utils/configurator/Configurator.h" +#include "modules/utils/currentcontrol/CurrentControl.h" +//#include "modules/utils/player/Player.h" +#include "modules/utils/killbutton/KillButton.h" +#include "modules/utils/PlayLed/PlayLed.h" +//#include "modules/utils/panel/Panel.h" +//#include "libs/Network/uip/Network.h" #include "Config.h" #include "checksumm.h" #include "ConfigValue.h" @@ -29,9 +51,18 @@ //#include "libs/USBDevice/DFU.h" //#include "libs/SDFAT.h" #include "StreamOutputPool.h" -#include "MotorDriverControl.h" +#include "ToolManager.h" #include "SEGGER_SYSVIEW.h" +#include "version.h" + +#include "mbed.h" + +#define second_usb_serial_enable_checksum CHECKSUM("second_usb_serial_enable") +#define disable_msd_checksum CHECKSUM("msd_disable") +#define dfu_enable_checksum CHECKSUM("dfu_enable") +#define watchdog_timeout_checksum CHECKSUM("watchdog_timeout") + DigitalOut leds[4] = { DigitalOut(LED1), DigitalOut(LED2), @@ -39,36 +70,177 @@ DigitalOut leds[4] = { DigitalOut(LED4) }; -int main() { +void init() { SEGGER_SYSVIEW_Conf(); - int cnt = 0; // Kernel creates modules, and receives and dispatches events between them Kernel* kernel = new Kernel(); - // Say hello ( TODO: Add back version and all ) - kernel->streams->printf("Smoothie2 dev\n"); + kernel->streams->printf("Smoothie Running @%ldMHz\r\n", SystemCoreClock / 1000000); + Version version; + kernel->streams->printf(" Build version %s, Build date %s\r\n", version.get_build(), version.get_build_date()); +#ifdef CNC + kernel->streams->printf(" CNC Build\r\n"); +#endif + +bool sdok= false; //TODO remove once SD Card code is working +// bool sdok= (sd.disk_initialize() == 0); +// if(!sdok) kernel->streams->printf("SDCard failed to initialize\r\n"); + +// #ifdef NONETWORK +// kernel->streams->printf("NETWORK is disabled\r\n"); +// #endif + +//#ifdef DISABLEMSD +// // attempt to be able to disable msd in config +// if(sdok && !kernel->config->value( disable_msd_checksum )->by_default(false)->as_bool()){ +// // HACK to zero the memory USBMSD uses as it and its objects seem to not initialize properly in the ctor +// size_t n= sizeof(USBMSD); +// void *v = AHB0.alloc(n); +// memset(v, 0, n); // clear the allocated memory +// msc= new(v) USBMSD(&u, &sd); // allocate object using zeroed memory +// }else{ +// msc= NULL; +// kernel->streams->printf("MSD is disabled\r\n"); +// } +//#endif // Create and add main modules - kernel->add_module( new Endstops() ); - kernel->add_module( new Laser() ); - //kernel->add_module( new ZProbe() ); - //kernel->add_module( new RotaryDeltaCalibration() ); - kernel->add_module( new MotorDriverControl(0) ); +// kernel->add_module( new Player() ); - // Create all Switch modules - SwitchPool *sp= new SwitchPool(); - sp->load_tools(); - delete sp; +// kernel->add_module( new CurrentControl() ); +// kernel->add_module( new KillButton() ); +// kernel->add_module( new PlayLed() ); - // Create all TemperatureControl modules. Note order is important here must be after extruder so Tn as a parameter will get executed first - TemperatureControlPool *tp= new TemperatureControlPool(); - tp->load_tools(); - delete tp; + // these modules can be completely disabled in the Makefile by adding to EXCLUDE_MODULES + #ifndef NO_TOOLS_ENDSTOPS + kernel->add_module( new Endstops() ); + #endif - // Clear the configuration cache as it is no longer needed - kernel->config->config_cache_clear(); + #ifndef NO_TOOLS_SWITCH + SwitchPool *sp= new SwitchPool(); + sp->load_tools(); + delete sp; + #endif + #ifndef NO_TOOLS_EXTRUDER + // NOTE this must be done first before Temperature control so ToolManager can handle Tn before temperaturecontrol module does + ExtruderMaker *em= new ExtruderMaker(); + em->load_tools(); + delete em; + #endif + #ifndef NO_TOOLS_TEMPERATURECONTROL + // Note order is important here must be after extruder so Tn as a parameter will get executed first + TemperatureControlPool *tp= new TemperatureControlPool(); + tp->load_tools(); + delete tp; + #endif + #ifndef NO_TOOLS_LASER + kernel->add_module( new Laser() ); + #endif + #ifndef NO_TOOLS_SPINDLE + kernel->add_module( new Spindle() ); + #endif + #ifndef NO_UTILS_PANEL +// kernel->add_module( new Panel() ); + #endif + #ifndef NO_TOOLS_ZPROBE + kernel->add_module( new ZProbe() ); + #endif + #ifndef NO_TOOLS_SCARACAL +// kernel->add_module( newSCARAcal() ); + #endif + #ifndef NO_TOOLS_ROTARYDELTACALIBRATION + kernel->add_module( new RotaryDeltaCalibration() ); + #endif + #ifndef NONETWORK +// kernel->add_module( new Network() ); + #endif + #ifndef NO_TOOLS_TEMPERATURESWITCH + // Must be loaded after TemperatureControl + kernel->add_module( new TemperatureSwitch() ); + #endif + #ifndef NO_TOOLS_DRILLINGCYCLES + kernel->add_module( new Drillingcycles() ); + #endif + #ifndef NO_TOOLS_FILAMENTDETECTOR + kernel->add_module( new FilamentDetector() ); + #endif + #ifndef NO_UTILS_MOTORDRIVERCONTROL + kernel->add_module( new MotorDriverControl(0) ); + #endif +// // Create and initialize USB stuff +// u.init(); +//#ifdef DISABLEMSD +// if(sdok && msc != NULL){ +// kernel->add_module( msc ); +// } +//#else +// kernel->add_module( &msc ); +//#endif + +// kernel->add_module( &usbserial ); +// if( kernel->config->value( second_usb_serial_enable_checksum )->by_default(false)->as_bool() ){ +// kernel->add_module( new(AHB0) USBSerial(&u) ); +// } + +// if( kernel->config->value( dfu_enable_checksum )->by_default(false)->as_bool() ){ +// kernel->add_module( new(AHB0) DFU(&u)); +// } + +// // 10 second watchdog timeout (or config as seconds) +// float t= kernel->config->value( watchdog_timeout_checksum )->by_default(10.0F)->as_number(); +// if(t > 0.1F) { +// // NOTE setting WDT_RESET with the current bootloader would leave it in DFU mode which would be suboptimal +// kernel->add_module( new Watchdog(t*1000000, WDT_MRI)); // WDT_RESET)); +// kernel->streams->printf("Watchdog enabled for %f seconds\n", t); +// }else{ + kernel->streams->printf("WARNING Watchdog is disabled\n"); +// } + + +// kernel->add_module( &u ); + + // memory before cache is cleared + //SimpleShell::print_mem(kernel->streams); + + // clear up the config cache to save some memory + kernel->config->config_cache_clear(); + + if(kernel->is_using_leds()) { + // set some leds to indicate status... led0 init done, led1 mainloop running, led2 idle loop running, led3 sdcard ok + leds[0]= 1; // indicate we are done with init + leds[3]= sdok?1:0; // 4th led indicates sdcard is available (TODO maye should indicate config was found) + } + + if(sdok) { + // load config override file if present + // NOTE only Mxxx commands that set values should be put in this file. The file is generated by M500 + FILE *fp= fopen(kernel->config_override_filename(), "r"); + if(fp != NULL) { + char buf[132]; + kernel->streams->printf("Loading config override file: %s...\n", kernel->config_override_filename()); + while(fgets(buf, sizeof buf, fp) != NULL) { + kernel->streams->printf(" %s", buf); + if(buf[0] == ';') continue; // skip the comments + struct SerialMessage message= {&(StreamOutput::NullStream), buf}; + kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message); + } + kernel->streams->printf("config override file executed\n"); + fclose(fp); + } + } + + // start the timers and interrupts + THEKERNEL->conveyor->start(THEROBOT->get_number_registered_motors()); + THEKERNEL->step_ticker->start(); +//TODO IS this still required? THEKERNEL->slow_ticker->start(); +} + +int main() +{ + init(); + int cnt = 0; // Main loop while(1){ if(THEKERNEL->is_using_leds()) { From 4f6a8f7699c30b3f184ff4387eb0131ada50f55f Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 31 Oct 2016 11:24:46 +1300 Subject: [PATCH 29/66] modified: ../README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 479f0d7c..903c04c2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Current status as at 2016-10-31: * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * Some major lower level things need implementation ( USB, Ethernet, SDIO ). +* There some changes in `gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h` which appear to be driven by Silicon Labs changes to support their EFM32 range of ARM processor's lower power modes. This causes conflicts with `sleep` and I have had to slightly modify this file to get our code to compile and work properly. I don't have time to go back and investigate this at this time. # Compiling From 5796fea4461415f03bbdbba915d5d8488eafc41b Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 31 Oct 2016 18:50:01 +1300 Subject: [PATCH 30/66] modified: ../README.md modified: libs/Debug/HardFault_HandlerC.c modified: main.cpp --- README.md | 4 +- src/libs/Debug/HardFault_HandlerC.c | 92 +++++++++++++++++++++++------ src/main.cpp | 4 +- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 903c04c2..9bc6fe40 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,10 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED Current status as at 2016-10-31: -* Issue: HardFault in `planner.cpp` on line 71 `block->steps[i] = labs(steps);` +* Issue: HardFault in `planner.cpp` on line 71 `block->steps[i] = labs(steps);` *FIXED* +* Issue: The HardFault_Handler was not being correctly called *FIXED* * Issue: No initial serial output on `main.ccp` on line 50 `kernel->streams->printf("Smoothie2 dev\n");` +* Issue: The G28 Homing only moves a short distance then stops. * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * Some major lower level things need implementation ( USB, Ethernet, SDIO ). diff --git a/src/libs/Debug/HardFault_HandlerC.c b/src/libs/Debug/HardFault_HandlerC.c index 57c85ae3..120e08f7 100644 --- a/src/libs/Debug/HardFault_HandlerC.c +++ b/src/libs/Debug/HardFault_HandlerC.c @@ -1,35 +1,83 @@ /** * HardFaultHandler_C: + * * This is called from the HardFault_HandlerAsm with a pointer the Fault stack * as the parameter. We can then read the values from the stack and place them * into local variables for ease of reading. + * * We then read the various Fault Status and Address Registers to help decode * cause of the fault. * The function ends with a BKPT instruction to force control back into the debugger * * Refer to http://support.code-red-tech.com/CodeRedWiki/DebugHardFault * + * And to http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html + * * Also refer to: * Cortex M4 exception handling * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/ch03s09s01.html * Cortex M0 exception handing * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/BABBFABJ.html + * + * The first register of interest is the program counter. + * + * In the code, the variable stacked_pc contains the program counter value. + * When the fault is a precise fault, the stacked_pc holds the address of the instruction that was executing when the hard fault + * (or other fault) occurred. When the fault is an imprecise fault, then additional steps are required to find the address of the + * instruction that caused the fault. + * + * To find the instruction at the address held in the stacked_pc variable, either... + * Open an assembly code window in the debugger, and manually enter the address to view the assembly instructions at that address, or + * Open the break point window in the debugger, and manually define an execution or access break point at that address. + * With the break point set, restart the application to see which line of code the instruction relates to. + * + * Knowing the instruction that was being executed when the fault occurred allows you to determine which other register values are + * also of interest. + * + * For example, if the instruction was using the value of R7 as an address, then the value of R7 needs to be know. + * Further, examining the assembly code, and the C code that generated the assembly code, will show what R7 actually holds + * (it might be the value of a variable, for example). + * + * Handling Imprecise Faults: + * + * ARM Cortex-M faults can be precise or imprecise. + * + * If the IMPRECISERR bit (bit 2) is set in the Bus Fault Status Register (or BFSR, which is at address 0xE000ED29), then the fault + * is imprecise. + * + * It is harder to determine the cause of an imprecise fault because the fault will not necessarily occur concurrently with the + * instruction that caused the fault. + * + * For example, if writes to memory are cached then there might be a delay between an assembly instruction initiating a write to + * memory and the write to memory actually occurring. + * + * If such a delayed write operation is invalid (for example, a write is being attempted to an invalid memory location) then an + * imprecise fault will occur, and the program counter value obtained using the code above will not be the address of the assembly + * instruction that initiated the write operation. + * + * Turn off write buffering by setting the DISDEFWBUF bit (bit 1) in the Auxiliary Control Register (or ACTLR) compile and + * re-run the test and then the imprecise fault becoming a precise fault, which makes the fault easier to debug. + * + * Note: this will come at the cost of slower program execution. */ + +// This overrides the WEAK definition of HardFault_Handler void HardFault_HandlerC(unsigned long *hardfault_args){ volatile unsigned long stacked_r0 ; volatile unsigned long stacked_r1 ; volatile unsigned long stacked_r2 ; volatile unsigned long stacked_r3 ; volatile unsigned long stacked_r12 ; - volatile unsigned long stacked_lr ; - volatile unsigned long stacked_pc ; - volatile unsigned long stacked_psr ; + volatile unsigned long stacked_lr ; // Link register + volatile unsigned long stacked_pc ; // Program counter + volatile unsigned long stacked_psr ; // Program status register volatile unsigned long _CFSR ; volatile unsigned long _HFSR ; volatile unsigned long _DFSR ; volatile unsigned long _AFSR ; volatile unsigned long _BFAR ; volatile unsigned long _MMAR ; + volatile unsigned long _BFSR ; stacked_r0 = ((unsigned long)hardfault_args[0]) ; stacked_r1 = ((unsigned long)hardfault_args[1]) ; @@ -57,27 +105,33 @@ void HardFault_HandlerC(unsigned long *hardfault_args){ // Check BFARVALID/MMARVALID to see if they are valid values // MemManage Fault Address Register _MMAR = (*((volatile unsigned long *)(0xE000ED34))) ; + // Bus Fault Address Register _BFAR = (*((volatile unsigned long *)(0xE000ED38))) ; + // Bus Fault Status Register + _BFSR = (*((volatile unsigned long *)(0xE000ED29))) ; + __asm("BKPT #0\n") ; // Break into the debugger } -__attribute__((naked)) -PE_ISR(Cpu_ivINT_Hard_Fault) +/* The prototype shows it is a naked function - in effect this is just an +assembly function. */ +static void HardFault_Handler( void ) __attribute__( ( naked ) ); + +/* The fault handler implementation calls a function called +prvGetRegistersFromStack(). */ +static void HardFault_Handler(void) { - __asm volatile ( - " movs r0,#4 \n" - " movs r1, lr \n" - " tst r0, r1 \n" - " beq _MSP \n" - " mrs r0, psp \n" - " b _HALT \n" - "_MSP: \n" - " mrs r0, msp \n" - "_HALT: \n" - " ldr r1,[r0,#20] \n" - " b HardFault_HandlerC \n" - " bkpt #0 \n" - ); + __asm volatile + ( + " tst lr, #4 \n" + " ite eq \n" + " mrseq r0, msp \n" + " mrsne r0, psp \n" + " ldr r1, [r0, #24] \n" + " ldr r2, handler2_address_const \n" + " bx r2 \n" + " handler2_address_const: .word HardFault_HandlerCk \n" + ); } diff --git a/src/main.cpp b/src/main.cpp index e64099a8..bc53a014 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -181,11 +181,11 @@ bool sdok= false; //TODO remove once SD Card code is working // kernel->add_module( &usbserial ); // if( kernel->config->value( second_usb_serial_enable_checksum )->by_default(false)->as_bool() ){ -// kernel->add_module( new(AHB0) USBSerial(&u) ); +// kernel->add_module( new USBSerial(&u) ); // } // if( kernel->config->value( dfu_enable_checksum )->by_default(false)->as_bool() ){ -// kernel->add_module( new(AHB0) DFU(&u)); +// kernel->add_module( new DFU(&u)); // } // // 10 second watchdog timeout (or config as seconds) From 985f37e377918f7543b5bcf05f3d0ba53e14e334 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 31 Oct 2016 18:50:22 +1300 Subject: [PATCH 31/66] modified: libs/Debug/HardFault_HandlerC.c --- src/libs/Debug/HardFault_HandlerC.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/libs/Debug/HardFault_HandlerC.c b/src/libs/Debug/HardFault_HandlerC.c index 120e08f7..4377de89 100644 --- a/src/libs/Debug/HardFault_HandlerC.c +++ b/src/libs/Debug/HardFault_HandlerC.c @@ -1,12 +1,13 @@ /** * HardFaultHandler_C: * - * This is called from the HardFault_HandlerAsm with a pointer the Fault stack - * as the parameter. We can then read the values from the stack and place them - * into local variables for ease of reading. + * This is called from the HardFault_Handler which over-rides the WEAK definition of HardFault_Handler with a pointer the + * Fault Stack as the parameter. + * + * Then this code reads the values from the stack and places them into local variables so they can be examined. + * + * There are various Fault Status and Address Registers to help decode the cause of the fault. * - * We then read the various Fault Status and Address Registers to help decode - * cause of the fault. * The function ends with a BKPT instruction to force control back into the debugger * * Refer to http://support.code-red-tech.com/CodeRedWiki/DebugHardFault @@ -14,14 +15,13 @@ * And to http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html * * Also refer to: - * Cortex M4 exception handling - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/ch03s09s01.html - * Cortex M0 exception handing - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/BABBFABJ.html + * Cortex M4 exception handling: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/ch03s09s01.html + * Cortex M0 exception handing: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/BABBFABJ.html * * The first register of interest is the program counter. * - * In the code, the variable stacked_pc contains the program counter value. + * In the code, below the variable stacked_pc contains the program counter value. + * * When the fault is a precise fault, the stacked_pc holds the address of the instruction that was executing when the hard fault * (or other fault) occurred. When the fault is an imprecise fault, then additional steps are required to find the address of the * instruction that caused the fault. @@ -59,6 +59,11 @@ * re-run the test and then the imprecise fault becoming a precise fault, which makes the fault easier to debug. * * Note: this will come at the cost of slower program execution. + * + * I hope this helps you track down this hard to find HardFaults + * + * Douglas.Pearless@gmail.com + * */ // This overrides the WEAK definition of HardFault_Handler From 4ae2377de9b72a3cece29123e616e38e91e65b82 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Tue, 1 Nov 2016 17:33:41 +1300 Subject: [PATCH 32/66] modified: README.md modified: src/config.default modified: src/modules/robot/Robot.cpp --- README.md | 4 ++-- src/config.default | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9bc6fe40..dea11c89 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Current status as at 2016-10-31: * Issue: HardFault in `planner.cpp` on line 71 `block->steps[i] = labs(steps);` *FIXED* * Issue: The HardFault_Handler was not being correctly called *FIXED* * Issue: No initial serial output on `main.ccp` on line 50 `kernel->streams->printf("Smoothie2 dev\n");` -* Issue: The G28 Homing only moves a short distance then stops. +* Issue: Movement (G0 Zn, G28 Homing) only moves a short distance then stops *CURRENTLY UNDER INVESTIGATION 2016-10-31* * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * Some major lower level things need implementation ( USB, Ethernet, SDIO ). @@ -46,7 +46,7 @@ And finally compile the code # TODO :Ā  Current major TODOs :Ā  - * Complete the three way comparison between 'old Smoothie - pre-major rewrite in June/July 2016' 'Smoothie V1 (current)' and 'SmoothieV2' + * Complete the three way comparison between 'old Smoothie - pre-major rewrite in June/July 2016' 'Smoothie V1 (current)' and 'SmoothieV2' *Mostly done as at 2016-10-31* * SD-Card, USB and Ethernet need to be implemented. * Move the step generation to the M0 co-processor instead of on the M4 main core. diff --git a/src/config.default b/src/config.default index dcd04a8c..9a9020b3 100644 --- a/src/config.default +++ b/src/config.default @@ -78,9 +78,9 @@ delta_mirror_xy true # true for firepi # For a 0.9 degree stepper motor and an TMC2130 driver set for 256 microsteps = (400 * 256 * 9.52226215)/360 = 2708.554567 # For a 1.8 degree stepper motor and an TMC2130 driver set for 256 microsteps = (200 * 256 * 9.52226215)/360 = 1354.277284 -alpha_steps_per_mm 338.0 # Steps per mm for alpha stepper -beta_steps_per_mm 338.0 # Steps per mm for beta stepper -gamma_steps_per_mm 338.0 # Steps per mm for gamma stepper +alpha_steps_per_mm 2708.554567 # Steps per mm for alpha stepper +beta_steps_per_mm 2708.554567 # Steps per mm for beta stepper +gamma_steps_per_mm 2708.554567 # Steps per mm for gamma stepper # Stepper module pins ( ports, and pin numbers, appending "!" to the number will invert a pin ) alpha_step_pin 5.0 # Pin for alpha stepper step signal From eab55b85d6acc0208b148ec7ea27fa03ba32de21 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Tue, 1 Nov 2016 17:34:09 +1300 Subject: [PATCH 33/66] modified: src/modules/robot/Robot.cpp --- src/modules/robot/Robot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/robot/Robot.cpp b/src/modules/robot/Robot.cpp index dc6eea6b..d9cf882b 100755 --- a/src/modules/robot/Robot.cpp +++ b/src/modules/robot/Robot.cpp @@ -178,7 +178,7 @@ void Robot::load_config() // in mm/sec but specified in config as mm/min this->max_speeds[X_AXIS] = THEKERNEL->config->value(x_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; this->max_speeds[Y_AXIS] = THEKERNEL->config->value(y_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; - this->max_speeds[Z_AXIS] = THEKERNEL->config->value(z_axis_max_speed_checksum )->by_default( 300.0F)->as_number() / 60.0F; + this->max_speeds[Z_AXIS] = THEKERNEL->config->value(z_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; this->segment_z_moves = THEKERNEL->config->value(segment_z_moves_checksum )->by_default(true)->as_bool(); this->save_g92 = THEKERNEL->config->value(save_g92_checksum )->by_default(false)->as_bool(); From 4f719a18e68c77f004fb3bae9c2056ff4379b917 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Wed, 2 Nov 2016 18:53:54 +1300 Subject: [PATCH 34/66] modified: config.default modified: libs/StepTicker.cpp modified: modules/robot/Planner.cpp --- src/config.default | 6 +++--- src/libs/StepTicker.cpp | 2 -- src/modules/robot/Planner.cpp | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/config.default b/src/config.default index 9a9020b3..3198610e 100644 --- a/src/config.default +++ b/src/config.default @@ -258,15 +258,15 @@ rdelta_homing true # forces all three alpha_min_endstop 7.4!^ # alpha_max_endstop 7.7^ # add ! to invert pullup if switch is NO to ground alpha_homing_direction home_to_max # Home up -alpha_max 300 # +alpha_max 3000 # beta_min_endstop 7.5!^ # beta_max_endstop 4.3^ # beta_homing_direction home_to_max # -beta_max 300 # +beta_max 30000 # gamma_min_endstop 4.1!^ # gamma_max_endstop 4.4^ # gamma_homing_direction home_to_max # -gamma_max 300 # +gamma_max 30000 # # optional enable limit switches, actions will stop if any enabled limit switch is triggered alpha_limit_enable false # set to true to enable X min and max limit switches diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 657acae7..8a355efe 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -75,8 +75,6 @@ StepTicker::StepTicker() this->running = false; this->current_block = nullptr; - this->start(); - #ifdef STEPTICKER_DEBUG_PIN // setup debug pin if defined stepticker_debug_pin.output(); diff --git a/src/modules/robot/Planner.cpp b/src/modules/robot/Planner.cpp index d4b86ed1..1432e44a 100644 --- a/src/modules/robot/Planner.cpp +++ b/src/modules/robot/Planner.cpp @@ -68,8 +68,6 @@ bool Planner::append_block( ActuatorCoordinates &actuator_pos, uint8_t n_motors, // find direction block->direction_bits[i] = (steps < 0) ? 1 : 0; // save actual steps in block - uint32_t a = labs(steps); - block->steps[i] = a; block->steps[i] = labs(steps); } From 9271850de6b09ff0460ef46f90dd74c4af9d6b2a Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Wed, 2 Nov 2016 18:58:33 +1300 Subject: [PATCH 35/66] modified: ../README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dea11c89..78c102bc 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,12 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED -Current status as at 2016-10-31: +Current status as at 2016-11-02: +* HardFault when initialising the `kernel` *FIXED* * Issue: HardFault in `planner.cpp` on line 71 `block->steps[i] = labs(steps);` *FIXED* * Issue: The HardFault_Handler was not being correctly called *FIXED* -* Issue: No initial serial output on `main.ccp` on line 50 `kernel->streams->printf("Smoothie2 dev\n");` -* Issue: Movement (G0 Zn, G28 Homing) only moves a short distance then stops *CURRENTLY UNDER INVESTIGATION 2016-10-31* +* Issue: No initial serial output on `main.ccp` on line 50 `kernel->streams->printf("Smoothie2 dev\n");` *CURRENTLY UNDER INVESTIGATION 2016-11-02* +* Issue: Movement (G0 Zn, G28 Homing) only moves a short distance then stops *CURRENTLY UNDER INVESTIGATION 2016-11-02* * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * Some major lower level things need implementation ( USB, Ethernet, SDIO ). From 9c02f59d0c2af866e162a1fabe4c61b1504022a4 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Wed, 2 Nov 2016 23:12:33 +1300 Subject: [PATCH 36/66] modified: libs/SlowTicker.cpp modified: libs/SlowTicker.h --- src/libs/SlowTicker.cpp | 103 +++++++++++++++++++++++++++++++++++++--- src/libs/SlowTicker.h | 17 +++++-- 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/libs/SlowTicker.cpp b/src/libs/SlowTicker.cpp index 52440bbd..90f36eee 100644 --- a/src/libs/SlowTicker.cpp +++ b/src/libs/SlowTicker.cpp @@ -11,36 +11,127 @@ using namespace std; #include "libs/Module.h" #include "libs/Kernel.h" #include "SlowTicker.h" +#include "StepTicker.h" #include "libs/Hook.h" +#include "modules/robot/Conveyor.h" +#include "Gcode.h" #include "mbed.h" +#include + // This module uses a Timer to periodically call hooks // Modules register with a function ( callback ) and a frequency, and we then call that function at the given frequency. SlowTicker* global_slow_ticker; SlowTicker::SlowTicker(){ - this->max_frequency = 0; global_slow_ticker = this; - this->ticker = new Ticker(); + //this->ticker = new Ticker(); + + // ISP button FIXME: WHy is this here? + ispbtn.from_string("2.10")->as_input()->pull_up(); + + //LPC_SC->PCONP |= (1 << 22); // Power Ticker ON + /* Enable timer 0 clock and reset it */ + LPC_CCU1->CLKCCU[CLK_MX_TIMER2].CFG |= 1; + LPC_TIMER2->MCR = 3; // Match on MR0, reset on MR0 + // do not enable interrupt until setup is complete + LPC_TIMER2->TCR = 0; // Disable interrupt + + max_frequency = 5; // initial max frequency is set to 5Hz + set_frequency(max_frequency); + flag_1s_flag = 0; +} + +void SlowTicker::start() +{ + LPC_TIMER2->TCR = 1; // Enable interrupt + NVIC_EnableIRQ(TIMER2_IRQn); // Enable interrupt handler +} + +void SlowTicker::on_module_loaded(){ + register_for_event(ON_IDLE); } // Set the base frequency we use for all sub-frequencies void SlowTicker::set_frequency( int frequency ){ - this->interval = (float)1 / frequency; // SystemCoreClock/4 = Timer increments in a second - this->ticker->attach_us(this, &SlowTicker::tick,(int)( this->interval * (float)1000000) ); + this->interval = (float)1 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second + LPC_TIMER2->MR[0] = this->interval; + LPC_TIMER2->TCR = 3; // Reset + LPC_TIMER2->TCR = 1; // Reset + flag_1s_count= SystemCoreClock>>2; +// this->ticker->attach_us(this, &SlowTicker::tick,(int)( this->interval * (float)1000000) ); } // The actual interrupt being called by the timer, this is where work is done void SlowTicker::tick(){ - // Call all hooks that need to be called ( bresenham ) + // Call all hooks that need to be called for (Hook* hook : this->hooks){ hook->countdown -= this->interval; - if (hook->countdown < (float)0) + if (hook->countdown < 0) { hook->countdown += hook->interval; hook->call(); } } + + // deduct tick time from second counter + flag_1s_count -= this->interval; + // if a whole second has elapsed, + if (flag_1s_count < 0) + { + // add a second to our counter + flag_1s_count += SystemCoreClock >> 2; + // and set a flag for idle event to pick up + flag_1s_flag++; + } + + // Enter MRI mode if the ISP button is pressed + // TODO: This should have it's own module + if (ispbtn.get() == 0) + __debugbreak(); + } + +bool SlowTicker::flag_1s(){ + // atomic flag check routine + // first disable interrupts + __disable_irq(); + // then check for a flag + if (flag_1s_flag) + { + // if we have a flag, decrement the counter + flag_1s_flag--; + // re-enable interrupts + __enable_irq(); + // and tell caller that we consumed a flag + return true; + } + // if no flag, re-enable interrupts and return false + __enable_irq(); + return false; +} + +extern DigitalOut leds[]; +void SlowTicker::on_idle(void*) +{ + static uint16_t ledcnt= 0; + if(THEKERNEL->is_using_leds()) { + // flash led 3 to show we are alive + leds[2]= (ledcnt++ & 0x1000) ? 1 : 0; + } + + // if interrupt has set the 1 second flag + if (flag_1s()) + // fire the on_second_tick event + THEKERNEL->call_event(ON_SECOND_TICK); +} + +extern "C" void TIMER2_IRQHandler (void){ + if((LPC_TIMER2->IR >> 0) & 1){ // If interrupt register set for MR0 + LPC_TIMER2->IR |= 1 << 0; // Reset it + } + global_slow_ticker->tick(); +} + diff --git a/src/libs/SlowTicker.h b/src/libs/SlowTicker.h index 8dd76b13..621fec41 100644 --- a/src/libs/SlowTicker.h +++ b/src/libs/SlowTicker.h @@ -24,6 +24,10 @@ using namespace std; class SlowTicker : public Module{ public: SlowTicker(); + + void on_module_loaded(void); + void on_idle(void*); + void start(); void set_frequency( int frequency ); void tick(); // For some reason this can't go in the .cpp, see : http://mbed.org/forum/mbed/topic/2774/?page=1#comment-14221 @@ -45,11 +49,18 @@ class SlowTicker : public Module{ return hook; } - // TOADDBACK:Ā private: + private: + bool flag_1s(); + vector hooks; - double max_frequency; - double interval; + uint32_t max_frequency; + uint32_t interval; Ticker* ticker; + + Pin ispbtn; +protected: + int flag_1s_count; + volatile int flag_1s_flag; }; From 87b5304aa2d2c7a6e1b52214d2a9faccac2c058a Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Wed, 2 Nov 2016 23:25:04 +1300 Subject: [PATCH 37/66] modified: ../README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 78c102bc..3cf62954 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,7 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED Current status as at 2016-11-02: -* HardFault when initialising the `kernel` *FIXED* -* Issue: HardFault in `planner.cpp` on line 71 `block->steps[i] = labs(steps);` *FIXED* -* Issue: The HardFault_Handler was not being correctly called *FIXED* -* Issue: No initial serial output on `main.ccp` on line 50 `kernel->streams->printf("Smoothie2 dev\n");` *CURRENTLY UNDER INVESTIGATION 2016-11-02* -* Issue: Movement (G0 Zn, G28 Homing) only moves a short distance then stops *CURRENTLY UNDER INVESTIGATION 2016-11-02* +* * Moved open issues to issue tracker * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * Some major lower level things need implementation ( USB, Ethernet, SDIO ). From 0d5f6d395729241906b63d22584a35d70b9b654c Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Wed, 2 Nov 2016 23:25:50 +1300 Subject: [PATCH 38/66] modified: ../README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cf62954..f572fc6e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED Current status as at 2016-11-02: -* * Moved open issues to issue tracker +* Moved open issues to issue tracker * This code base is equivalent to Smoothie V1 as at 2016-10-18. * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * Some major lower level things need implementation ( USB, Ethernet, SDIO ). From 2b42fb7afc36e5a9ce15e828b676314b8ac34641 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 3 Nov 2016 00:16:14 +1300 Subject: [PATCH 39/66] modified: libs/Kernel.cpp modified: makefile --- src/libs/Kernel.cpp | 13 ------------- src/makefile | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 4163181c..b673affe 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -56,19 +56,6 @@ Kernel::Kernel(){ // All streams register to the Kernel so we can broadcast messages this->streams = new StreamOutputPool(); -#ifndef SMOOTHIE_UART_PRIMARY_ENABLE -#define SMOOTHIE_UART_PRIMARY_ENABLE 0 -#endif - -#ifndef SMOOTHIE_UART_SECONDARY_ENABLE -#define SMOOTHIE_UART_SECONDARY_ENABLE 0 -#endif - -#ifndef DEFAULT_SERIAL_BAUD_RATE -#define DEFAULT_SERIAL_BAUD_RATE 9600 -#endif - - // Create the default UART Serial Console interface if (SMOOTHIE_UART_PRIMARY_ENABLE) { this->serial = new SerialConsole(SMOOTHIE_UART_PRIMARY_TX, SMOOTHIE_UART_PRIMARY_RX, DEFAULT_SERIAL_BAUD_RATE); diff --git a/src/makefile b/src/makefile index 3900ec06..180ee8b9 100644 --- a/src/makefile +++ b/src/makefile @@ -41,7 +41,7 @@ MBED_LIBS += USBHost # Use C++11 features for the checksums DEFINES += -DCHECKSUM_USE_CPP - +DEFINES += -DDEFAULT_SERIAL_BAUD_RATE=9600 # Need to use a custom LPC4337 script for boards like the Smoothie2 hardware which use that chip instead of the LPC4330. ifeq "$(BOARD)" "Smoothie2Proto1" From 3ed4b98f4bab68f4c19f744dce10befcf001ee9a Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 6 Nov 2016 14:36:26 +1300 Subject: [PATCH 40/66] modified: libs/Kernel.cpp modified: libs/SlowTicker.cpp modified: libs/StepTicker.cpp modified: main.cpp modified: makefile --- src/libs/Kernel.cpp | 57 +++++++++++++++++++++-------------------- src/libs/SlowTicker.cpp | 10 ++++++-- src/libs/StepTicker.cpp | 8 +++--- src/main.cpp | 3 ++- src/makefile | 2 +- 5 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index b673affe..116a9b1f 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -73,10 +73,11 @@ Kernel::Kernel(){ this->config->config_cache_load(); // now config is loaded we can do normal setup for serial based on config - delete this->serial; - this->serial= NULL; - - this->streams = new StreamOutputPool(); +//TODO TOADDBACK when USB is implemented +// delete this->serial; +// this->serial= NULL; +// +// this->streams = new StreamOutputPool(); this->current_path = "/"; @@ -127,30 +128,30 @@ Kernel::Kernel(){ // TODO : These should go into platform-specific files // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 -/* - NVIC_SetPriorityGrouping(0); - NVIC_SetPriority(TIMER0_IRQn, 2); - NVIC_SetPriority(TIMER1_IRQn, 1); - NVIC_SetPriority(TIMER2_IRQn, 4); - NVIC_SetPriority(PendSV_IRQn, 3); - - // Set other priorities lower than the timers - NVIC_SetPriority(ADC_IRQn, 5); - NVIC_SetPriority(USB_IRQn, 5); - - // If MRI is enabled - if( MRI_ENABLE ){ - if( NVIC_GetPriority(UART0_IRQn) > 0 ){ NVIC_SetPriority(UART0_IRQn, 5); } - if( NVIC_GetPriority(UART1_IRQn) > 0 ){ NVIC_SetPriority(UART1_IRQn, 5); } - if( NVIC_GetPriority(UART2_IRQn) > 0 ){ NVIC_SetPriority(UART2_IRQn, 5); } - if( NVIC_GetPriority(UART3_IRQn) > 0 ){ NVIC_SetPriority(UART3_IRQn, 5); } - }else{ - NVIC_SetPriority(UART0_IRQn, 5); - NVIC_SetPriority(UART1_IRQn, 5); - NVIC_SetPriority(UART2_IRQn, 5); - NVIC_SetPriority(UART3_IRQn, 5); - } -*/ + +// NVIC_SetPriorityGrouping(0); +// NVIC_SetPriority(TIMER0_IRQn, 2); +// NVIC_SetPriority(TIMER1_IRQn, 1); +// NVIC_SetPriority(TIMER2_IRQn, 4); +// NVIC_SetPriority(PendSV_IRQn, 3); + +// // Set other priorities lower than the timers +// NVIC_SetPriority(ADC_IRQn, 5); +// NVIC_SetPriority(USB_IRQn, 5); +// +// // If MRI is enabled +// if( MRI_ENABLE ){ +// if( NVIC_GetPriority(UART0_IRQn) > 0 ){ NVIC_SetPriority(UART0_IRQn, 5); } +// if( NVIC_GetPriority(UART1_IRQn) > 0 ){ NVIC_SetPriority(UART1_IRQn, 5); } +// if( NVIC_GetPriority(UART2_IRQn) > 0 ){ NVIC_SetPriority(UART2_IRQn, 5); } +// if( NVIC_GetPriority(UART3_IRQn) > 0 ){ NVIC_SetPriority(UART3_IRQn, 5); } +// }else{ +// NVIC_SetPriority(UART0_IRQn, 5); +// NVIC_SetPriority(UART1_IRQn, 5); +// NVIC_SetPriority(UART2_IRQn, 5); +// NVIC_SetPriority(UART3_IRQn, 5); +// } + // Configure the step ticker this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(1)->as_number(); diff --git a/src/libs/SlowTicker.cpp b/src/libs/SlowTicker.cpp index 90f36eee..0e1c2c75 100644 --- a/src/libs/SlowTicker.cpp +++ b/src/libs/SlowTicker.cpp @@ -19,6 +19,9 @@ using namespace std; #include +#include "SEGGER_SYSVIEW.h" +#include "SEGGER_RTT_Conf.h" + // This module uses a Timer to periodically call hooks // Modules register with a function ( callback ) and a frequency, and we then call that function at the given frequency. @@ -26,7 +29,7 @@ SlowTicker* global_slow_ticker; SlowTicker::SlowTicker(){ global_slow_ticker = this; - //this->ticker = new Ticker(); + this->ticker = new Ticker(); // ISP button FIXME: WHy is this here? ispbtn.from_string("2.10")->as_input()->pull_up(); @@ -55,7 +58,8 @@ void SlowTicker::on_module_loaded(){ // Set the base frequency we use for all sub-frequencies void SlowTicker::set_frequency( int frequency ){ - this->interval = (float)1 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second + this->interval = (float)1 / frequency; + //this->interval = (float)1000000 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second LPC_TIMER2->MR[0] = this->interval; LPC_TIMER2->TCR = 3; // Reset LPC_TIMER2->TCR = 1; // Reset @@ -129,9 +133,11 @@ void SlowTicker::on_idle(void*) } extern "C" void TIMER2_IRQHandler (void){ + SEGGER_SYSVIEW_RecordEnterISR(); if((LPC_TIMER2->IR >> 0) & 1){ // If interrupt register set for MR0 LPC_TIMER2->IR |= 1 << 0; // Reset it } global_slow_ticker->tick(); + SEGGER_SYSVIEW_RecordExitISR(); } diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 8a355efe..634759be 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -50,7 +50,7 @@ StepTicker::StepTicker() LPC_TIMER0->TCR = 0; // Disable interrupt LPC_TIMER0->PR = prescale - 1; LPC_TIMER0->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER0->MCR |= 3; // Match on MR0, reset on MR0 + LPC_TIMER0->MCR |= 3; // Interrupt on MR0, reset on MR0 NVIC_SetPriority(TIMER0_IRQn,0); /* Enable timer 1 clock and reset it */ @@ -63,7 +63,7 @@ StepTicker::StepTicker() LPC_TIMER1->TCR = 0; // Disable interrupt LPC_TIMER1->PR = prescale - 1; LPC_TIMER1->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER1->MCR |= 5; // match on Mr0, stop on match + LPC_TIMER1->MCR |= 5; // Interrupt on MR0, stop on MR0 // Default start values this->set_frequency(100000); @@ -143,11 +143,11 @@ extern "C" void TIMER1_IRQHandler (void) extern "C" void TIMER0_IRQHandler (void) { // SEGGER_RTT_LOCK(); -// SEGGER_SYSVIEW_RecordEnterISR(); + SEGGER_SYSVIEW_RecordEnterISR(); LPC_TIMER0->IR |= 1 << 0; StepTicker::getInstance()->step_tick(); NVIC_ClearPendingIRQ(TIMER0_IRQn); -// SEGGER_SYSVIEW_RecordExitISR(); + SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } diff --git a/src/main.cpp b/src/main.cpp index bc53a014..f52a182a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -234,7 +234,7 @@ bool sdok= false; //TODO remove once SD Card code is working // start the timers and interrupts THEKERNEL->conveyor->start(THEROBOT->get_number_registered_motors()); THEKERNEL->step_ticker->start(); -//TODO IS this still required? THEKERNEL->slow_ticker->start(); + THEKERNEL->slow_ticker->start(); } int main() @@ -249,6 +249,7 @@ int main() } THEKERNEL->call_event(ON_MAIN_LOOP); THEKERNEL->call_event(ON_IDLE); + THEKERNEL->streams->printf("0"); } } diff --git a/src/makefile b/src/makefile index 180ee8b9..721b0ecb 100644 --- a/src/makefile +++ b/src/makefile @@ -64,7 +64,7 @@ ifeq "$(BOARD)" "Bambino210E" DEFINES += -DSMOOTHIE_UART_PRIMARY_ENABLE=1 DEFINES += -DSMOOTHIE_UART_PRIMARY_TX=P2_10 DEFINES += -DSMOOTHIE_UART_PRIMARY_RX=P2_11 -DEFINES += -DSMOOTHIE_UART_SECONDARY_ENABLE=1 +DEFINES += -DSMOOTHIE_UART_SECONDARY_ENABLE=0 DEFINES += -DSMOOTHIE_UART_SECONDARY_TX=P6_4 DEFINES += -DSMOOTHIE_UART_SECONDARY_RX=P6_5 DEFINES += -DTARGET_BAMBINO210E From 121cd8f5be671e8da3ddcd449c755047a7a292ad Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 7 Nov 2016 22:56:42 +1300 Subject: [PATCH 41/66] modified: ../README.md modified: generate-version.sh modified: libs/Kernel.cpp modified: libs/SlowTicker.cpp modified: libs/StepTicker.cpp modified: main.cpp modified: makefile --- README.md | 9 ------- src/generate-version.sh | 2 +- src/libs/Kernel.cpp | 52 ++++++++++++++++++++--------------------- src/libs/SlowTicker.cpp | 10 ++------ src/libs/StepTicker.cpp | 8 +++---- src/main.cpp | 30 ++++++------------------ src/makefile | 7 +++--- 7 files changed, 44 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index a386f6e8..f572fc6e 100644 --- a/README.md +++ b/README.md @@ -43,18 +43,9 @@ And finally compile the code # TODO :Ā  Current major TODOs :Ā  -<<<<<<< HEAD * Complete the three way comparison between 'old Smoothie - pre-major rewrite in June/July 2016' 'Smoothie V1 (current)' and 'SmoothieV2' *Mostly done as at 2016-10-31* * SD-Card, USB and Ethernet need to be implemented. * Move the step generation to the M0 co-processor instead of on the M4 main core. -======= - * Porting of existing functionality that hasn't been ported yet ( anything in https://github.com/Smoothieware/Smoothieware/tree/edge/src/modules that isn't ported yet ) - * Some modules have been ported, but the underlying low-level/HAL stuff to talk to the peripherals isn't ( like SPI, or ADCs etc ), it's just dummy objects right now. So that needs porting too. - * USB and Ethernet need to be implemented. - * Doing the step generation on the M0 co-processor instead of on the M4 main core. - * This port was forked from Smoothie months ago, it should be updated to incoprorate changes to Smoothie done in the meantime. - * Explore using http://nuttx.org/ for a RTOS ( major rewrite ) ->>>>>>> 65fb13e36461556a69d4c824ccb3b4420f1f10af TODOĀ functionality to port from v1 in more detail : * libs/Adc.cpp : Port low level ADC functionality, then re-enable it in TemperatureControl ( note :Ā there is some weird pin configuration surrounding this that makes it more complicated than it seems, but I don't remember what it is. Logxen@gmail.com probably remembers what it is ). diff --git a/src/generate-version.sh b/src/generate-version.sh index c58098b9..cf1bea3c 100755 --- a/src/generate-version.sh +++ b/src/generate-version.sh @@ -1,2 +1,2 @@ echo `git symbolic-ref HEAD 2> /dev/null | cut -b 12-`-`git log --pretty=format:%h -1` -touch version.cpp \ No newline at end of file +touch version.cpp diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 116a9b1f..c8967db0 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -73,11 +73,11 @@ Kernel::Kernel(){ this->config->config_cache_load(); // now config is loaded we can do normal setup for serial based on config -//TODO TOADDBACK when USB is implemented +//TOADDBACK when the USB libraries are ported // delete this->serial; // this->serial= NULL; // -// this->streams = new StreamOutputPool(); +// this->streams = new StreamOutputPool(); this->current_path = "/"; @@ -128,30 +128,30 @@ Kernel::Kernel(){ // TODO : These should go into platform-specific files // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 - -// NVIC_SetPriorityGrouping(0); -// NVIC_SetPriority(TIMER0_IRQn, 2); -// NVIC_SetPriority(TIMER1_IRQn, 1); -// NVIC_SetPriority(TIMER2_IRQn, 4); -// NVIC_SetPriority(PendSV_IRQn, 3); - -// // Set other priorities lower than the timers -// NVIC_SetPriority(ADC_IRQn, 5); -// NVIC_SetPriority(USB_IRQn, 5); -// -// // If MRI is enabled -// if( MRI_ENABLE ){ -// if( NVIC_GetPriority(UART0_IRQn) > 0 ){ NVIC_SetPriority(UART0_IRQn, 5); } -// if( NVIC_GetPriority(UART1_IRQn) > 0 ){ NVIC_SetPriority(UART1_IRQn, 5); } -// if( NVIC_GetPriority(UART2_IRQn) > 0 ){ NVIC_SetPriority(UART2_IRQn, 5); } -// if( NVIC_GetPriority(UART3_IRQn) > 0 ){ NVIC_SetPriority(UART3_IRQn, 5); } -// }else{ -// NVIC_SetPriority(UART0_IRQn, 5); -// NVIC_SetPriority(UART1_IRQn, 5); -// NVIC_SetPriority(UART2_IRQn, 5); -// NVIC_SetPriority(UART3_IRQn, 5); -// } - +/* + NVIC_SetPriorityGrouping(0); + NVIC_SetPriority(TIMER0_IRQn, 2); + NVIC_SetPriority(TIMER1_IRQn, 1); + NVIC_SetPriority(TIMER2_IRQn, 4); + NVIC_SetPriority(PendSV_IRQn, 3); + + // Set other priorities lower than the timers + NVIC_SetPriority(ADC_IRQn, 5); + NVIC_SetPriority(USB_IRQn, 5); + + // If MRI is enabled + if( MRI_ENABLE ){ + if( NVIC_GetPriority(UART0_IRQn) > 0 ){ NVIC_SetPriority(UART0_IRQn, 5); } + if( NVIC_GetPriority(UART1_IRQn) > 0 ){ NVIC_SetPriority(UART1_IRQn, 5); } + if( NVIC_GetPriority(UART2_IRQn) > 0 ){ NVIC_SetPriority(UART2_IRQn, 5); } + if( NVIC_GetPriority(UART3_IRQn) > 0 ){ NVIC_SetPriority(UART3_IRQn, 5); } + }else{ + NVIC_SetPriority(UART0_IRQn, 5); + NVIC_SetPriority(UART1_IRQn, 5); + NVIC_SetPriority(UART2_IRQn, 5); + NVIC_SetPriority(UART3_IRQn, 5); + } +*/ // Configure the step ticker this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(1)->as_number(); diff --git a/src/libs/SlowTicker.cpp b/src/libs/SlowTicker.cpp index 0e1c2c75..90f36eee 100644 --- a/src/libs/SlowTicker.cpp +++ b/src/libs/SlowTicker.cpp @@ -19,9 +19,6 @@ using namespace std; #include -#include "SEGGER_SYSVIEW.h" -#include "SEGGER_RTT_Conf.h" - // This module uses a Timer to periodically call hooks // Modules register with a function ( callback ) and a frequency, and we then call that function at the given frequency. @@ -29,7 +26,7 @@ SlowTicker* global_slow_ticker; SlowTicker::SlowTicker(){ global_slow_ticker = this; - this->ticker = new Ticker(); + //this->ticker = new Ticker(); // ISP button FIXME: WHy is this here? ispbtn.from_string("2.10")->as_input()->pull_up(); @@ -58,8 +55,7 @@ void SlowTicker::on_module_loaded(){ // Set the base frequency we use for all sub-frequencies void SlowTicker::set_frequency( int frequency ){ - this->interval = (float)1 / frequency; - //this->interval = (float)1000000 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second + this->interval = (float)1 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second LPC_TIMER2->MR[0] = this->interval; LPC_TIMER2->TCR = 3; // Reset LPC_TIMER2->TCR = 1; // Reset @@ -133,11 +129,9 @@ void SlowTicker::on_idle(void*) } extern "C" void TIMER2_IRQHandler (void){ - SEGGER_SYSVIEW_RecordEnterISR(); if((LPC_TIMER2->IR >> 0) & 1){ // If interrupt register set for MR0 LPC_TIMER2->IR |= 1 << 0; // Reset it } global_slow_ticker->tick(); - SEGGER_SYSVIEW_RecordExitISR(); } diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 634759be..8a355efe 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -50,7 +50,7 @@ StepTicker::StepTicker() LPC_TIMER0->TCR = 0; // Disable interrupt LPC_TIMER0->PR = prescale - 1; LPC_TIMER0->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER0->MCR |= 3; // Interrupt on MR0, reset on MR0 + LPC_TIMER0->MCR |= 3; // Match on MR0, reset on MR0 NVIC_SetPriority(TIMER0_IRQn,0); /* Enable timer 1 clock and reset it */ @@ -63,7 +63,7 @@ StepTicker::StepTicker() LPC_TIMER1->TCR = 0; // Disable interrupt LPC_TIMER1->PR = prescale - 1; LPC_TIMER1->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER1->MCR |= 5; // Interrupt on MR0, stop on MR0 + LPC_TIMER1->MCR |= 5; // match on Mr0, stop on match // Default start values this->set_frequency(100000); @@ -143,11 +143,11 @@ extern "C" void TIMER1_IRQHandler (void) extern "C" void TIMER0_IRQHandler (void) { // SEGGER_RTT_LOCK(); - SEGGER_SYSVIEW_RecordEnterISR(); +// SEGGER_SYSVIEW_RecordEnterISR(); LPC_TIMER0->IR |= 1 << 0; StepTicker::getInstance()->step_tick(); NVIC_ClearPendingIRQ(TIMER0_IRQn); - SEGGER_SYSVIEW_RecordExitISR(); +// SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } diff --git a/src/main.cpp b/src/main.cpp index 6f1adaf1..7adb1dd1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,25 +64,10 @@ #define watchdog_timeout_checksum CHECKSUM("watchdog_timeout") DigitalOut leds[4] = { -#ifdef TARGET_BAMBINO210E - DigitalOut(P6_11), - DigitalOut(P2_5), - DigitalOut(P6_1), - DigitalOut(P6_2) - -#elif defined(TARGET_BAMBINO200E) - DigitalOut(P6_11), - DigitalOut(P2_5), - DigitalOut(P6_11), // only has 2 leds - DigitalOut(P2_5) - -#elif defined(TARGET_SMOOTHIE2_PROTO1) - // smoothie 2 proto1 - DigitalOut(P1_1), - DigitalOut(P1_2), - DigitalOut(P2_8), - DigitalOut(P2_9) -#endif + DigitalOut(LED1), + DigitalOut(LED2), + DigitalOut(LED3), + DigitalOut(LED4) }; void init() { @@ -102,9 +87,9 @@ bool sdok= false; //TODO remove once SD Card code is working // bool sdok= (sd.disk_initialize() == 0); // if(!sdok) kernel->streams->printf("SDCard failed to initialize\r\n"); -// #ifdef NONETWORK -// kernel->streams->printf("NETWORK is disabled\r\n"); -// #endif + #ifdef NONETWORK + kernel->streams->printf("NETWORK is disabled\r\n"); + #endif //#ifdef DISABLEMSD // // attempt to be able to disable msd in config @@ -264,7 +249,6 @@ int main() } THEKERNEL->call_event(ON_MAIN_LOOP); THEKERNEL->call_event(ON_IDLE); - THEKERNEL->streams->printf("0"); } } diff --git a/src/makefile b/src/makefile index 721b0ecb..404fbce1 100644 --- a/src/makefile +++ b/src/makefile @@ -41,7 +41,8 @@ MBED_LIBS += USBHost # Use C++11 features for the checksums DEFINES += -DCHECKSUM_USE_CPP -DEFINES += -DDEFAULT_SERIAL_BAUD_RATE=9600 +#Disable the network +DEFINES += -DNONETWORK # Need to use a custom LPC4337 script for boards like the Smoothie2 hardware which use that chip instead of the LPC4330. ifeq "$(BOARD)" "Smoothie2Proto1" @@ -64,7 +65,7 @@ ifeq "$(BOARD)" "Bambino210E" DEFINES += -DSMOOTHIE_UART_PRIMARY_ENABLE=1 DEFINES += -DSMOOTHIE_UART_PRIMARY_TX=P2_10 DEFINES += -DSMOOTHIE_UART_PRIMARY_RX=P2_11 -DEFINES += -DSMOOTHIE_UART_SECONDARY_ENABLE=0 +DEFINES += -DSMOOTHIE_UART_SECONDARY_ENABLE=1 DEFINES += -DSMOOTHIE_UART_SECONDARY_TX=P6_4 DEFINES += -DSMOOTHIE_UART_SECONDARY_RX=P6_5 DEFINES += -DTARGET_BAMBINO210E @@ -93,7 +94,7 @@ endif # this is the default UART baud rate used if it is not set in config # it is also the baud rate used to report any errors found while parsing the config file -DEFAULT_SERIAL_BAUD_RATE?=9600 +DEFINES += -DDEFAULT_SERIAL_BAUD_RATE=9600 # generate a git version string ifneq "$(OS)" "Windows_NT" From d863bd6859220c4452d96875155af9ab29d1df6c Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 7 Nov 2016 22:57:07 +1300 Subject: [PATCH 42/66] modified: config.default --- src/config.default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.default b/src/config.default index 3198610e..f73ff3c8 100644 --- a/src/config.default +++ b/src/config.default @@ -31,7 +31,7 @@ junction_deviation 0.05 # Similar to the o # Stepper module configuration microseconds_per_step_pulse 5 # Duration of step pulses to stepper drivers, in microseconds -base_stepping_frequency 10000 #100000 # Base frequency for stepping +base_stepping_frequency 150000 #100000 # Base frequency for stepping # Cartesian axis speed limits x_axis_max_speed 30000 # mm/min From 947c88e70b7a086eb4efe69888b479dc1797d48b Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Mon, 7 Nov 2016 22:59:12 +1300 Subject: [PATCH 43/66] modified: main.cpp --- src/main.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7adb1dd1..99b385ea 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,10 +64,26 @@ #define watchdog_timeout_checksum CHECKSUM("watchdog_timeout") DigitalOut leds[4] = { - DigitalOut(LED1), - DigitalOut(LED2), - DigitalOut(LED3), - DigitalOut(LED4) +#ifdef TARGET_BAMBINO210E + DigitalOut(P6_11), + DigitalOut(P2_5), + DigitalOut(P6_1), + DigitalOut(P6_2) + +#elif defined(TARGET_BAMBINO200E) + DigitalOut(P6_11), + DigitalOut(P2_5), + DigitalOut(P6_11), // only has 2 leds + DigitalOut(P2_5) + +#elif defined(TARGET_SMOOTHIE2_PROTO1) + // smoothie 2 proto1 + DigitalOut(P1_1), + DigitalOut(P1_2), + DigitalOut(P2_8), + DigitalOut(P2_9) +#endif + }; void init() { From 125fa6b5a4728e242c5756ebc18631dbcee07da6 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Tue, 8 Nov 2016 09:00:51 +1300 Subject: [PATCH 44/66] modified: README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a386f6e8..f2f86bb2 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,11 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED -Current status as at 2016-11-02: +Current status as at 2016-11-07: +* SlowTicker code finally ported, but there are timing issues that need to be solved, including interrupt priorities to get it working properly (for end-stops for instance) +* Merged changes 65fb13e and c3f254c to bring codebase current with SMoothie2 main repo * Moved open issues to issue tracker -* This code base is equivalent to Smoothie V1 as at 2016-10-18. +* This code base is equivalent to Smoothie V1 as at 2016-10-18 and Smoothie V2 as at 2016-11-08 * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) * Some major lower level things need implementation ( USB, Ethernet, SDIO ). * There some changes in `gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h` which appear to be driven by Silicon Labs changes to support their EFM32 range of ARM processor's lower power modes. This causes conflicts with `sleep` and I have had to slightly modify this file to get our code to compile and work properly. I don't have time to go back and investigate this at this time. From 5039dd638d75527a1002e5bf341ba61fef104a3c Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 22 Oct 2016 16:54:12 +1300 Subject: [PATCH 45/66] modified: src/config.default modified: src/libs/Pin.cpp modified: src/libs/Pin.h modified: src/libs/StepTicker.cpp modified: src/libs/StepperMotor.cpp modified: src/libs/StepperMotor.h modified: src/libs/checksumm.h modified: src/libs/nuts_bolts.h modified: src/libs/utils.h modified: src/main.cpp modified: src/makefile modified: src/modules/robot/Planner.cpp modified: src/modules/robot/Robot.cpp modified: src/modules/robot/Robot.h modified: src/modules/robot/Stepper.cpp modified: src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp modified: src/modules/tools/endstops/Endstops.cpp modified: src/modules/tools/temperaturecontrol/max31855.cpp --- .gitignore | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d9256042..27e5908b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,16 @@ #eclipse project files .cproject .project - +.settings/ +gdbserver.log +src/Segger_SystemView/Global.h +src/Segger_SystemView/SEGGER.h +src/Segger_SystemView/SEGGER_RTT.c +src/Segger_SystemView/SEGGER_RTT.h +src/Segger_SystemView/SEGGER_RTT_Conf.h +src/Segger_SystemView/SEGGER_SYSVIEW.c +src/Segger_SystemView/SEGGER_SYSVIEW.h +src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h +src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h +src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c +src/Segger_SystemView/SEGGER_SYSVIEW_Int.h From 12be6ab865ddc21866f33e2e444f44a99166f84d Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Tue, 8 Nov 2016 10:34:20 +1300 Subject: [PATCH 46/66] modified: README.md --- README.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b6b8c186..d6bb3432 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,13 @@ Current status as at 2016-11-07: * Some major lower level things need implementation ( USB, Ethernet, SDIO ). * There some changes in `gcc4mbed/external/mbed/libraries/mbed/hal/sleep_api.h` which appear to be driven by Silicon Labs changes to support their EFM32 range of ARM processor's lower power modes. This causes conflicts with `sleep` and I have had to slightly modify this file to get our code to compile and work properly. I don't have time to go back and investigate this at this time. +There are a lot of bugs in the code and it is very much a work in progress, the reason it is published is to allow others to contriute before it is folded back into the Smoothie2 main repo. + +Current status : +* This code base is equivalent to Smoothie V1 as at 2016-10-18. +* The Extruder code is being worked on and as of 2016-10-22 and does not yet compile. +* Some major lower level things need implementation ( USB, Ethernet, SDIO ). + # Compiling First, go into gcc4mbed @@ -45,9 +52,8 @@ And finally compile the code # TODO :Ā  Current major TODOs :Ā  - * Complete the three way comparison between 'old Smoothie - pre-major rewrite in June/July 2016' 'Smoothie V1 (current)' and 'SmoothieV2' *Mostly done as at 2016-10-31* - * SD-Card, USB and Ethernet need to be implemented. - * Move the step generation to the M0 co-processor instead of on the M4 main core. + * SD-Card, USB and Ethernet need to be implemented, awaiting base libraries from Micromint.com. + * Move the step generation to the M0 co-processor instead of on the M4 main core OR into a FPGA. TODOĀ functionality to port from v1 in more detail : * libs/Adc.cpp : Port low level ADC functionality, then re-enable it in TemperatureControl ( note :Ā there is some weird pin configuration surrounding this that makes it more complicated than it seems, but I don't remember what it is. Logxen@gmail.com probably remembers what it is ). @@ -61,8 +67,15 @@ TODOĀ functionality to port from v1 in more detail : * modules/tools/filamentdetector : Get PWM to work * modules/tools/spindle :Ā Get PWM to work * modules/tools/temperaturecontrol : Finish porting, get the ADC to actually work, I cannot test the AD8495 temperature inputs as I don't have one of these! - * modules/tools/zprobe :Ā Test - * modules/tools/simpleshell :Ā Port + * modules/tools/zprobe :Ā Ported Needs testing (2016-11-08) + * modules/tools/drillingcycles : Ported Needs testing (2016-11-08) + * modules/tools/extruder :Ā Ported Needs testing (2016-11-08) + * modules/tools/filamentdetector : Port + * modules/tools/filamentdetector : Get PWM to work + * modules/tools/spindle :Ā Get PWM to work + * modules/tools/temperaturecontrol : Finish porting, get the ADC to actually work, port the AD8495 temperature input + * modules/tools/zprobe :Ā Ported Needs testing (2016-11-08) + * modules/tools/simpleshell :Ā Port once USB ported * The FPGA requires a SGPIO spi library * The FPGA requires a way to flash a .bin to it over JTAG from the M4 @@ -87,4 +100,4 @@ Planned refactors ( only to be done when the basic port is done ) :Ā  * Try to read config directly from SD card without cache ( as smoothie used to do ) now that we have SDIO * Queue refactor ( see wiki ) * Adding MTP and removing MSD for USB -* Full rewrite based on a RTOS +* Explore using http://nuttx.org/ for a RTOS ( major rewrite ) From b13114ae470b5e71039fa56708caa56cd4dbc401 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Tue, 8 Nov 2016 10:37:04 +1300 Subject: [PATCH 47/66] modified: README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6bb3432..481ade0c 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,10 @@ There are a lot of bugs in the code and it is very much a work in progress, the #IF YOU ARE NOT A CODER / WORKING ON THIS CODE BASE DO NOT TRY TO USE THIS UNTIL THE CODE HAS BEEN FULLY PORTED AND TESTED -Current status as at 2016-11-07: +Current status as at 2016-11-08: * SlowTicker code finally ported, but there are timing issues that need to be solved, including interrupt priorities to get it working properly (for end-stops for instance) -* Merged changes 65fb13e and c3f254c to bring codebase current with SMoothie2 main repo +* The current focus is getting all the timing and interrupts and interrupt priorities sorted, then continue testing of ported code. +* Merged changes 65fb13e and c3f254c to bring codebase current with Smoothie V2 main repo * Moved open issues to issue tracker * This code base is equivalent to Smoothie V1 as at 2016-10-18 and Smoothie V2 as at 2016-11-08 * The code that relies on the priority setting of GPIO interrupts has not had this feature migrated (e.g. laser) From 9581ab8398491a2ef8cbb1759f31cc9d7b233883 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Wed, 9 Nov 2016 22:40:49 +1300 Subject: [PATCH 48/66] modified: config.default modified: libs/Kernel.cpp new file: libs/MemoryPool.cpp new file: libs/MemoryPool.h modified: libs/SlowTicker.cpp modified: libs/StepTicker.cpp modified: libs/StepTicker.h new file: libs/platform_memory.cpp new file: libs/platform_memory.h modified: modules/tools/endstops/Endstops.cpp modified: modules/tools/laser/Laser.cpp modified: modules/tools/laser/Laser.h --- src/config.default | 70 ++----- src/libs/Kernel.cpp | 24 ++- src/libs/MemoryPool.cpp | 244 ++++++++++++++++++++++++ src/libs/MemoryPool.h | 58 ++++++ src/libs/SlowTicker.cpp | 21 +- src/libs/StepTicker.cpp | 73 +++---- src/libs/StepTicker.h | 6 +- src/libs/platform_memory.cpp | 4 + src/libs/platform_memory.h | 12 ++ src/modules/tools/endstops/Endstops.cpp | 2 +- src/modules/tools/laser/Laser.cpp | 12 +- src/modules/tools/laser/Laser.h | 4 +- 12 files changed, 408 insertions(+), 122 deletions(-) create mode 100644 src/libs/MemoryPool.cpp create mode 100644 src/libs/MemoryPool.h create mode 100644 src/libs/platform_memory.cpp create mode 100644 src/libs/platform_memory.h diff --git a/src/config.default b/src/config.default index f73ff3c8..2ed0b583 100644 --- a/src/config.default +++ b/src/config.default @@ -31,12 +31,12 @@ junction_deviation 0.05 # Similar to the o # Stepper module configuration microseconds_per_step_pulse 5 # Duration of step pulses to stepper drivers, in microseconds -base_stepping_frequency 150000 #100000 # Base frequency for stepping +base_stepping_frequency 100000 # Base frequency for stepping # Cartesian axis speed limits -x_axis_max_speed 30000 # mm/min -y_axis_max_speed 30000 # mm/min -z_axis_max_speed 30000 # mm/min +x_axis_max_speed 60000 # mm/min +y_axis_max_speed 60000 # mm/min +z_axis_max_speed 60000 # mm/min # Arm solution configuration : Rotatable Delta robot. Translates mm positions into stepper positions arm_solution rotary_delta # selects the delta arm solution @@ -87,57 +87,19 @@ alpha_step_pin 5.0 # Pin for alpha st alpha_dir_pin 6.12 # Pin for alpha stepper direction alpha_en_pin 4.10 # Pin for alpha enable pin alpha_current 1.5 # X stepper motor current -alpha_max_rate 30000.0 # mm/min +alpha_max_rate 60000.0 # mm/min beta_step_pin 5.7 # Pin for beta stepper step signal beta_dir_pin 5.5 # Pin for beta stepper direction beta_en_pin 7.6 # Pin for beta enable beta_current 1.5 # Y stepper motor current -beta_max_rate 30000.0 # mm/min +beta_max_rate 60000.0 # mm/min gamma_step_pin 4.0 # Pin for gamma stepper step signal gamma_dir_pin 1.7 # Pin for gamma stepper direction gamma_en_pin 6.9 # Pin for gamma enable gamma_current 1.5 # Z stepper motor current -gamma_max_rate 30000.0 # mm/min - -#SilentStepStick TMC2130 setup -motor_driver_control.alpha.enable true # alpha (X) is a TMC2130 -motor_driver_control.alpha.designator A # A to set the settings -motor_driver_control.alpha.chip TMC2130 # chip name -motor_driver_control.alpha.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 -motor_driver_control.alpha.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 -motor_driver_control.alpha.microsteps 256 # microsteps -motor_driver_control.alpha.alarm true # set to true means the error bits are checked -motor_driver_control.alpha.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set -motor_driver_control.alpha.spi_channel 0 # SPI channel 1 is sdcard channel -motor_driver_control.alpha.spi_cs_pin 7.4 # SPI CS pin -#motor_driver_control.alpha.spi_frequency 1000000 # SPI frequency - -motor_driver_control.beta.enable true # beta (Y) is a TMC2130 -motor_driver_control.beta.designator B # B to set the settings -motor_driver_control.beta.chip TMC2130 # chip name -motor_driver_control.beta.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 -motor_driver_control.beta.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 -motor_driver_control.beta.microsteps 256 # microsteps -motor_driver_control.beta.alarm true # set to true means the error bits are checked -motor_driver_control.beta.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set -motor_driver_control.beta.spi_channel 0 # SPI channel 1 is sdcard channel -motor_driver_control.beta.spi_cs_pin 7.5 # SPI CS pin -#motor_driver_control.beta.spi_frequency 1000000 # SPI frequency - -motor_driver_control.gamma.enable true # gamma (Z) is a TMC2130 -motor_driver_control.gamma.designator C # C to set the settings -motor_driver_control.gamma.chip TMC2130 # chip name -motor_driver_control.gamma.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 -motor_driver_control.gamma.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 -motor_driver_control.gamma.microsteps 256 # microsteps -motor_driver_control.gamma.alarm true # set to true means the error bits are checked -motor_driver_control.gamma.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set -motor_driver_control.gamma.spi_channel 0 # SPI channel 1 is sdcard channel ??? -motor_driver_control.gamma.spi_cs_pin 1.0 # SPI CS pin -#motor_driver_control.gamma.spi_frequency 1000000 # SPI frequency - +gamma_max_rate 60000.0 # mm/min ## System configuration # Serial communications configuration ( baud rate defaults to 9600 if undefined ) @@ -258,27 +220,27 @@ rdelta_homing true # forces all three alpha_min_endstop 7.4!^ # alpha_max_endstop 7.7^ # add ! to invert pullup if switch is NO to ground alpha_homing_direction home_to_max # Home up -alpha_max 3000 # +alpha_max 200 # beta_min_endstop 7.5!^ # beta_max_endstop 4.3^ # beta_homing_direction home_to_max # -beta_max 30000 # +beta_max 200 # gamma_min_endstop 4.1!^ # gamma_max_endstop 4.4^ # gamma_homing_direction home_to_max # -gamma_max 30000 # +gamma_max 200 # # optional enable limit switches, actions will stop if any enabled limit switch is triggered -alpha_limit_enable false # set to true to enable X min and max limit switches -beta_limit_enable false # set to true to enable Y min and max limit switches -gamma_limit_enable false # set to true to enable Z min and max limit switches +alpha_limit_enable false # set to true to enable X min and max limit switches +beta_limit_enable false # set to true to enable Y min and max limit switches +gamma_limit_enable false # set to true to enable Z min and max limit switches alpha_fast_homing_rate_mm_s 500 # feedrates in mm/second beta_fast_homing_rate_mm_s 500 # " gamma_fast_homing_rate_mm_s 500 # " -alpha_slow_homing_rate_mm_s 20 # " -beta_slow_homing_rate_mm_s 20 # " -gamma_slow_homing_rate_mm_s 20 # " +alpha_slow_homing_rate_mm_s 250 # " +beta_slow_homing_rate_mm_s 250 # " +gamma_slow_homing_rate_mm_s 250 # " alpha_homing_retract_mm 5 # distance in mm beta_homing_retract_mm 5 # " diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index c8967db0..babc8fbf 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -12,7 +12,7 @@ #include "libs/SlowTicker.h" #include "libs/Adc.h" #include "libs/StreamOutputPool.h" -//#include +#include #include "checksumm.h" #include "ConfigValue.h" @@ -29,7 +29,7 @@ #include "Configurator.h" //#include "SimpleShell.h" -//#include "platform_memory.h" +#include "platform_memory.h" #include #include @@ -72,6 +72,8 @@ Kernel::Kernel(){ // Pre-load the config cache, do after setting up serial so we can report errors to serial this->config->config_cache_load(); + // For slow repeteative tasks + this->add_module( this->slow_ticker = new SlowTicker()); // now config is loaded we can do normal setup for serial based on config //TOADDBACK when the USB libraries are ported // delete this->serial; @@ -118,6 +120,11 @@ Kernel::Kernel(){ // we exepct ok per line now not per G code, setting this to false will return to the old (incorrect) way of ok per G code this->ok_per_line= this->config->value( ok_per_line_checksum )->by_default(true)->as_bool(); + // Configure the step ticker + this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); + float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); + // REMOVE this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(1000)->as_number(); + this->add_module( this->serial ); // HAL stuff @@ -127,8 +134,6 @@ Kernel::Kernel(){ //TODO ADDBACK this->adc = new Adc(); // TODO : These should go into platform-specific files - // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 -/* NVIC_SetPriorityGrouping(0); NVIC_SetPriority(TIMER0_IRQn, 2); NVIC_SetPriority(TIMER1_IRQn, 1); @@ -136,7 +141,10 @@ Kernel::Kernel(){ NVIC_SetPriority(PendSV_IRQn, 3); // Set other priorities lower than the timers - NVIC_SetPriority(ADC_IRQn, 5); + NVIC_SetPriority(ADC0_IRQn, 5); + NVIC_SetPriority(ADC1_IRQn, 5); + +/* NVIC_SetPriority(USB_IRQn, 5); // If MRI is enabled @@ -152,10 +160,6 @@ Kernel::Kernel(){ NVIC_SetPriority(UART3_IRQn, 5); } */ - // Configure the step ticker - this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); - float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(1)->as_number(); - // Configure the step ticker this->step_ticker->set_frequency( this->base_stepping_frequency ); this->step_ticker->set_unstep_time( microseconds_per_step_pulse ); @@ -249,7 +253,7 @@ void Kernel::call_event(_EVENT_ENUM id_event, void * argument){ bool was_idle= true; if(id_event == ON_HALT) { this->halted= (argument == nullptr); - was_idle= conveyor->is_queue_empty(); // see if we were doing anything like printing + was_idle= conveyor->is_idle(); // see if we were doing anything like printing } // send to all registered modules diff --git a/src/libs/MemoryPool.cpp b/src/libs/MemoryPool.cpp new file mode 100644 index 00000000..60474ec4 --- /dev/null +++ b/src/libs/MemoryPool.cpp @@ -0,0 +1,244 @@ +#include "MemoryPool.h" + +#include "StreamOutput.h" + +#include +#include + +// this catches all usages of delete blah. The object's destructor is called before we get here +// it first checks if the deleted object is part of a pool, and uses free otherwise. +void operator delete(void* p) +{ + MemoryPool* m = MemoryPool::first; + while (m) + { + if (m->has(p)) + { + MDEBUG("Pool %p has %p, using dealloc()\n", m, p); + m->dealloc(p); + return; + } + m = m->next; + } + + MDEBUG("no pool has %p, using free()\n", p); + free(p); +} + + +#define offset(x) (((uint8_t*) x) - ((uint8_t*) this->base)) + +typedef struct __attribute__ ((packed)) +{ + uint32_t next :31; + uint32_t used :1; + + uint8_t data[]; +} _poolregion; + +MemoryPool* MemoryPool::first = NULL; + +MemoryPool::MemoryPool(void* base, uint16_t size) +{ + this->base = base; + this->size = size; + + ((_poolregion*) base)->used = 0; + ((_poolregion*) base)->next = size; + + // insert ourselves into head of LL + next = first; + first = this; +} + +MemoryPool::~MemoryPool() +{ + MDEBUG("Pool %p destroyed: region %p (%d)\n", this, base, size); + + // remove ourselves from the LL + if (first == this) + { // special case: we're first + first = this->next; + return; + } + + // otherwise search the LL for the previous pool + MemoryPool* m = first; + while (m) + { + if (m->next == this) + { + m->next = next; + return; + } + m = m->next; + } +} + +void* MemoryPool::alloc(size_t nbytes) +{ + // nbytes = ceil(nbytes / 4) * 4 + if (nbytes & 3) + nbytes += 4 - (nbytes & 3); + + // start at the start + _poolregion* p = ((_poolregion*) base); + + // find the allocation size including our metadata + uint16_t nsize = nbytes + sizeof(_poolregion); + + MDEBUG("\tallocate %d bytes from %p\n", nsize, base); + + // now we walk the list, looking for a sufficiently large free block + do { + MDEBUG("\t\tchecking %p (%s, %db)\n", p, (p->used?"used":"free"), p->next); + if ((p->used == 0) && (p->next >= nsize)) + { // we found a free space that's big enough + MDEBUG("\t\tFOUND free block at %p (%+d) with %d bytes\n", p, offset(p), p->next); + // mark it as used + p->used = 1; + + // if there's free space at the end of this block + if (p->next > nsize) + { + // q = p->next + _poolregion* q = (_poolregion*) (((uint8_t*) p) + nsize); + + MDEBUG("\t\twriting header to %p (%+d) (%d)\n", q, offset(q), p->next - nsize); + // write a new block header into q + q->used = 0; + q->next = p->next - nsize; + + // set our next to point to it + p->next = nsize; + + // sanity check + if (offset(q) >= size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + } + + // then return the data region for the block + return &p->data; + } + + // p = p->next + p = (_poolregion*) (((uint8_t*) p) + p->next); + + // make sure we don't walk off the end + } while (p <= (_poolregion*) (((uint8_t*)base) + size)); + + // fell off the end of the region! + return NULL; +} + +void MemoryPool::dealloc(void* d) +{ + _poolregion* p = (_poolregion*) (((uint8_t*) d) - sizeof(_poolregion)); + p->used = 0; + + MDEBUG("\tdeallocating %p (%+d, %db)\n", p, offset(p), p->next); + + // combine next block if it's free + _poolregion* q = (_poolregion*) (((uint8_t*) p) + p->next); + if (q->used == 0) + { + MDEBUG("\t\tCombining with next free region at %p, new size is %d\n", q, p->next + q->next); + + // sanity check + if (offset(q) > size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + + p->next += q->next; + } + + // walk the list to find previous block + q = (_poolregion*) base; + do { + // check if q is the previous block + if ((((uint8_t*) q) + q->next) == (uint8_t*) p) { + // q is the previous block. + if (q->used == 0) + { // if q is free + MDEBUG("\t\tCombining with previous free region at %p, new size is %d\n", q, p->next + q->next); + + // combine! + q->next += p->next; + + // sanity check + if ((offset(p) + p->next) >= size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + } + + // we found previous block, return + return; + } + + // return if last block + if (offset(q) + q->next >= size) + return; + + // q = q->next + q = (_poolregion*) (((uint8_t*) q) + q->next); + + // if some idiot deallocates our memory region while we're using it, strange things can happen. + // avoid an infinite loop in that case, however we'll still leak memory and may corrupt things + if (q->next == 0) + return; + + // make sure we don't walk off the end + } while (q < (_poolregion*) (((uint8_t*) base) + size)); +} + +void MemoryPool::debug(StreamOutput* str) +{ + _poolregion* p = (_poolregion*) base; + uint32_t tot = 0; + uint32_t free = 0; + str->printf("Start: %ub MemoryPool at %p\n", size, p); + do { + str->printf("\tChunk at %p (%+4d): %s, %lu bytes\n", p, offset(p), (p->used?"used":"free"), p->next); + tot += p->next; + if (p->used == 0) + free += p->next; + if ((offset(p) + p->next >= size) || (p->next <= sizeof(_poolregion))) + { + str->printf("End: total %lub, free: %lub\n", tot, free); + return; + } + p = (_poolregion*) (((uint8_t*) p) + p->next); + } while (1); +} + +bool MemoryPool::has(void* p) +{ + return ((p >= base) && (p < (void*) (((uint8_t*) base) + size))); +} + +uint32_t MemoryPool::free() +{ + uint32_t free = 0; + + _poolregion* p = (_poolregion*) base; + + do { + if (p->used == 0) + free += p->next; + if (offset(p) + p->next >= size) + return free; + if (p->next <= sizeof(_poolregion)) + return free; + p = (_poolregion*) (((uint8_t*) p) + p->next); + } while (1); +} diff --git a/src/libs/MemoryPool.h b/src/libs/MemoryPool.h new file mode 100644 index 00000000..a2a97357 --- /dev/null +++ b/src/libs/MemoryPool.h @@ -0,0 +1,58 @@ +#ifndef _MEMORYPOOL_H +#define _MEMORYPOOL_H + +#include +// #include +#include + +#ifdef MEMDEBUG + #define MDEBUG(...) printf(__VA_ARGS__) +#else + #define MDEBUG(...) do {} while (0) +#endif + +class StreamOutput; + +/* + * with MUCH thanks to http://www.parashift.com/c++-faq-lite/memory-pools.html + * + * test framework at https://gist.github.com/triffid/5563987 + */ + +class MemoryPool +{ +public: + MemoryPool(void* base, uint16_t size); + ~MemoryPool(); + + void* alloc(size_t); + void dealloc(void* p); + + void debug(StreamOutput*); + + bool has(void*); + + uint32_t free(void); + + MemoryPool* next; + + static MemoryPool* first; + +private: + void* base; + uint16_t size; +}; + +// this overloads "placement new" +inline void* operator new(size_t nbytes, MemoryPool& pool) +{ + return pool.alloc(nbytes); +} + +// this allows placement new to free memory if the constructor fails +inline void operator delete(void* p, MemoryPool& pool) +{ + pool.dealloc(p); +} + +#endif /* _MEMORYPOOL_H */ diff --git a/src/libs/SlowTicker.cpp b/src/libs/SlowTicker.cpp index 90f36eee..c22b0edd 100644 --- a/src/libs/SlowTicker.cpp +++ b/src/libs/SlowTicker.cpp @@ -26,17 +26,23 @@ SlowTicker* global_slow_ticker; SlowTicker::SlowTicker(){ global_slow_ticker = this; - //this->ticker = new Ticker(); // ISP button FIXME: WHy is this here? ispbtn.from_string("2.10")->as_input()->pull_up(); - //LPC_SC->PCONP |= (1 << 22); // Power Ticker ON - /* Enable timer 0 clock and reset it */ + uint32_t PCLK = SystemCoreClock; + uint32_t prescale = PCLK / 1000000; //Increment MR each uSecond + + /* Enable timer 1 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER2].CFG |= 1; - LPC_TIMER2->MCR = 3; // Match on MR0, reset on MR0 - // do not enable interrupt until setup is complete - LPC_TIMER2->TCR = 0; // Disable interrupt + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER2_RST & 31); //Trigger a peripheral reset for the timer + while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER2_RST & 31)))){} + /* Configure Timer 0 */ + LPC_TIMER2->CTCR = 0x0; // timer mode + LPC_TIMER2->TCR = 0; // Disable interrupt + LPC_TIMER2->PR = prescale - 1; + LPC_TIMER2->MR[0] = 10000000; // Initial dummy value for Match Register + LPC_TIMER2->MCR |= 3; // match on Mr0, stop on match max_frequency = 5; // initial max frequency is set to 5Hz set_frequency(max_frequency); @@ -55,12 +61,11 @@ void SlowTicker::on_module_loaded(){ // Set the base frequency we use for all sub-frequencies void SlowTicker::set_frequency( int frequency ){ - this->interval = (float)1 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second + this->interval = SystemCoreClock / frequency; // SystemCoreClock = Timer increments in a second LPC_TIMER2->MR[0] = this->interval; LPC_TIMER2->TCR = 3; // Reset LPC_TIMER2->TCR = 1; // Reset flag_1s_count= SystemCoreClock>>2; -// this->ticker->attach_us(this, &SlowTicker::tick,(int)( this->interval * (float)1000000) ); } // The actual interrupt being called by the timer, this is where work is done diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 8a355efe..84a226ac 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -16,7 +16,6 @@ #include "Block.h" #include "Conveyor.h" -//#include "system_LPC17xx.h" // mbed.h lib #include #include @@ -32,18 +31,23 @@ GPIO stepticker_debug_pin(STEPTICKER_DEBUG_PIN); #define SET_STEPTICKER_DEBUG_PIN(n) #endif + +// StepTicker handles the base frequency ticking for the Stepper Motors / Actuators +// It has a list of those, and calls their tick() functions at regular intervals +// They then do Bresenham stuff themselves + StepTicker *StepTicker::instance; -StepTicker::StepTicker() -{ +StepTicker::StepTicker(){ + instance = this; // setup the Singleton instance of the stepticker uint32_t PCLK = SystemCoreClock; - uint32_t prescale = PCLK / 10000000; + uint32_t prescale = PCLK / 1000000; //Increment MR each uSecond /* Enable timer 0 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER0].CFG |= 1; - LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER0_RST & 31); //Trigger a peripheral reset for the timer + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER0_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER0_RST & 31)))){} /* Configure Timer 0 */ LPC_TIMER0->CTCR = 0x0; // timer mode @@ -51,13 +55,11 @@ StepTicker::StepTicker() LPC_TIMER0->PR = prescale - 1; LPC_TIMER0->MR[0] = 10000000; // Initial dummy value for Match Register LPC_TIMER0->MCR |= 3; // Match on MR0, reset on MR0 - NVIC_SetPriority(TIMER0_IRQn,0); /* Enable timer 1 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER1].CFG |= 1; - LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER1_RST & 31)))){} - /* Configure Timer 1 */ LPC_TIMER1->CTCR = 0x0; // timer mode LPC_TIMER1->TCR = 0; // Disable interrupt @@ -65,9 +67,10 @@ StepTicker::StepTicker() LPC_TIMER1->MR[0] = 10000000; // Initial dummy value for Match Register LPC_TIMER1->MCR |= 5; // match on Mr0, stop on match + // Default start values this->set_frequency(100000); - this->set_unstep_time(100); + this->set_unstep_time(10); this->unstep.reset(); this->num_motors = 0; @@ -75,11 +78,12 @@ StepTicker::StepTicker() this->running = false; this->current_block = nullptr; - #ifdef STEPTICKER_DEBUG_PIN +#ifdef STEPTICKER_DEBUG_PIN // setup debug pin if defined stepticker_debug_pin.output(); stepticker_debug_pin= 0; - #endif +#endif + } StepTicker::~StepTicker() @@ -98,19 +102,18 @@ void StepTicker::start() void StepTicker::set_frequency( float frequency ) { this->frequency = frequency; - this->period_us = (int)lrint(1000000/frequency); + this->period = (int)lrint(1000000/frequency); - /* Update Timer 0 Match register and reset timer */ - LPC_TIMER0->MR[0] = this->period_us; - LPC_TIMER0->TCR = 3; // Reset - LPC_TIMER0->TCR = 1; // start + LPC_TIMER0->MR[0] = this->period; + LPC_TIMER0->TCR = 3; // Reset + LPC_TIMER0->TCR = 1; // start } -// Set the reset delay -void StepTicker::set_unstep_time( float microseconds ){ - this->reset_delay_us = (int)lrint(microseconds); - /* Update Timer 1 Match register */ - LPC_TIMER1->MR[0] = this->reset_delay_us; +// Set the reset delay, must be called after set_frequency +void StepTicker::set_unstep_time( float microseconds ) +{ + uint32_t delay = (int)lrint(microseconds); + LPC_TIMER1->MR[0] = delay; // TODO check that the unstep time is less than the step period, if not slow down step ticker } @@ -132,21 +135,24 @@ extern "C" void TIMER1_IRQHandler (void) { // SEGGER_RTT_LOCK(); // SEGGER_SYSVIEW_RecordEnterISR(); - LPC_TIMER1->IR |= 1 << 0; - StepTicker::getInstance()->unstep_tick(); - NVIC_ClearPendingIRQ(TIMER1_IRQn); + + LPC_TIMER1->IR |= 1 << 0; + StepTicker::getInstance()->unstep_tick(); + +// NVIC_ClearPendingIRQ(TIMER1_IRQn); // SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } -// Step timer interrupt handler +// The actual interrupt handler where we do all the work extern "C" void TIMER0_IRQHandler (void) { -// SEGGER_RTT_LOCK(); -// SEGGER_SYSVIEW_RecordEnterISR(); - LPC_TIMER0->IR |= 1 << 0; - StepTicker::getInstance()->step_tick(); - NVIC_ClearPendingIRQ(TIMER0_IRQn); +// SEGGER_RTT_LOCK(); +// SEGGER_SYSVIEW_RecordEnterISR(); + // Reset interrupt register + LPC_TIMER0->IR |= 1 << 0; + StepTicker::getInstance()->step_tick(); +// NVIC_ClearPendingIRQ(TIMER0_IRQn); // SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } @@ -245,10 +251,9 @@ void StepTicker::step_tick (void) // Note there could be a race here if we run another tick before the unsteps have happened, // right now it takes about 3-4us but if the unstep were near 10uS or greater it would be an issue // also it takes at least 2us to get here so even when set to 1us pulse width it will still be about 3us - if( this->unstep.any()){ - /* Start Unstep timer (Timer 1) */ - LPC_TIMER1->TCR = 0x3; // reset - LPC_TIMER1->TCR = 1; // enable = 1, reset = 0 + if( unstep.any()) { + LPC_TIMER1->TCR = 3; + LPC_TIMER1->TCR = 1; } diff --git a/src/libs/StepTicker.h b/src/libs/StepTicker.h index 63e443f7..39e21c1a 100644 --- a/src/libs/StepTicker.h +++ b/src/libs/StepTicker.h @@ -10,7 +10,7 @@ #pragma once #include -#include +#include #include #include #include @@ -46,10 +46,6 @@ class StepTicker{ static StepTicker *getInstance() { return instance; } - // TOADDBACK was private - volatile uint32_t tick_cnt; - uint32_t period_us; - uint32_t reset_delay_us; private: static StepTicker *instance; diff --git a/src/libs/platform_memory.cpp b/src/libs/platform_memory.cpp new file mode 100644 index 00000000..f80b287c --- /dev/null +++ b/src/libs/platform_memory.cpp @@ -0,0 +1,4 @@ +#include "platform_memory.h" + +MemoryPool* _AHB0; +MemoryPool* _AHB1; diff --git a/src/libs/platform_memory.h b/src/libs/platform_memory.h new file mode 100644 index 00000000..71ffe2c0 --- /dev/null +++ b/src/libs/platform_memory.h @@ -0,0 +1,12 @@ +#ifndef _PLATFORM_MEMORY_H +#define _PLATFORM_MEMORY_H + +#include "MemoryPool.h" + +#define AHB0 (*_AHB0) +#define AHB1 (*_AHB1) + +extern MemoryPool* _AHB0; +extern MemoryPool* _AHB1; + +#endif /* _PLATFORM_MEMORY_H */ diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index c36611bc..24a6aef9 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -834,7 +834,7 @@ void Endstops::on_gcode_received(void *argument) } break; -//TODO ADDBACK +//TOADDBACK /* // NOTE this is to test accuracy of lead screws etc. case 1910: { diff --git a/src/modules/tools/laser/Laser.cpp b/src/modules/tools/laser/Laser.cpp index 796338fb..04decc74 100644 --- a/src/modules/tools/laser/Laser.cpp +++ b/src/modules/tools/laser/Laser.cpp @@ -46,8 +46,9 @@ Laser::Laser() manual_fire= false; } -void Laser::on_module_loaded() { - if( !THEKERNEL->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ){ +void Laser::on_module_loaded() +{ + if( !THEKERNEL->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ) { // as not needed free up resource delete this; return; @@ -63,8 +64,7 @@ void Laser::on_module_loaded() { pwm_pin = dummy_pin->hardware_pwm(); - if (pwm_pin == nullptr) - { + if (pwm_pin == NULL) { // TOADDBACK THEKERNEL->streams->printf("Error: Laser cannot use P%d.%d (P2.0 - P2.5, P1.18, P1.20, P1.21, P1.23, P1.24, P1.26, P3.25, P3.26 only). Laser module disabled.\n", dummy_pin->port_number, dummy_pin->pin); delete dummy_pin; delete this; @@ -107,10 +107,6 @@ void Laser::on_module_loaded() { set_laser_power(0); //register for events - this->register_for_event(ON_GCODE_EXECUTE); - this->register_for_event(ON_SPEED_CHANGE); - this->register_for_event(ON_BLOCK_BEGIN); - this->register_for_event(ON_BLOCK_END); this->register_for_event(ON_HALT); this->register_for_event(ON_GCODE_RECEIVED); this->register_for_event(ON_CONSOLE_LINE_RECEIVED); diff --git a/src/modules/tools/laser/Laser.h b/src/modules/tools/laser/Laser.h index 7978e291..a1f104b8 100644 --- a/src/modules/tools/laser/Laser.h +++ b/src/modules/tools/laser/Laser.h @@ -38,7 +38,7 @@ class Laser : public Module{ float current_speed_ratio(const Block *block) const; mbed::PwmOut *pwm_pin; // PWM output to regulate the laser power - Pin *ttl_pin; // TTL output to fire laser + Pin *ttl_pin; // TTL output to fire laser float laser_maximum_power; // maximum allowed laser power to be output on the pwm pin float laser_minimum_power; // value used to tickle the laser on moves. Also minimum value for auto-scaling float laser_maximum_s_value; // Value of S code that will represent max power @@ -46,7 +46,7 @@ class Laser : public Module{ struct { bool laser_on:1; // set if the laser is on bool pwm_inverting:1; // stores whether the PWM period should be inverted - bool ttl_used:1; // stores whether we have a TTL output + bool ttl_used:1; // stores whether we have a TTL output bool ttl_inverting:1; // stores whether the TTL output should be inverted bool manual_fire:1; // set when manually firing }; From 6314505b93b562edfc18b80cac002218c88c3ba5 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 18:38:16 +1300 Subject: [PATCH 49/66] modified: src/libs/Kernel.cpp modified: src/libs/Kernel.h new file: src/libs/MemoryPool.cpp new file: src/libs/MemoryPool.h modified: src/libs/Pwm.cpp modified: src/libs/RingBuffer.h modified: src/libs/SlowTicker.cpp modified: src/libs/SlowTicker.h modified: src/libs/StepTicker.cpp modified: src/libs/StepTicker.h modified: src/libs/StepperMotor.cpp modified: src/libs/StepperMotor.h modified: src/libs/nuts_bolts.h new file: src/libs/platform_memory.cpp new file: src/libs/platform_memory.h modified: src/libs/utils.cpp modified: src/libs/utils.h modified: src/main.cpp modified: src/makefile modified: src/modules/robot/ActuatorCoordinates.h modified: src/modules/robot/Block.h modified: src/modules/robot/Conveyor.h modified: src/modules/robot/Planner.cpp modified: src/modules/tools/endstops/Endstops.cpp modified: src/modules/tools/endstops/Endstops.h modified: src/modules/tools/laser/Laser.cpp modified: src/modules/tools/laser/Laser.h modified: src/modules/tools/switch/Switch.cpp --- src/libs/Kernel.cpp | 23 ++- src/libs/Kernel.h | 2 + src/libs/MemoryPool.cpp | 244 ++++++++++++++++++++++++ src/libs/MemoryPool.h | 58 ++++++ src/libs/Pwm.cpp | 1 + src/libs/RingBuffer.h | 5 +- src/libs/SlowTicker.cpp | 29 +-- src/libs/SlowTicker.h | 4 +- src/libs/StepTicker.cpp | 77 ++++---- src/libs/StepTicker.h | 12 +- src/libs/StepperMotor.cpp | 2 +- src/libs/StepperMotor.h | 7 +- src/libs/nuts_bolts.h | 4 - src/libs/platform_memory.cpp | 4 + src/libs/platform_memory.h | 12 ++ src/libs/utils.cpp | 17 +- src/libs/utils.h | 39 ++-- src/main.cpp | 84 ++++---- src/makefile | 21 +- src/modules/robot/ActuatorCoordinates.h | 3 +- src/modules/robot/Block.h | 6 +- src/modules/robot/Conveyor.h | 6 +- src/modules/robot/Planner.cpp | 3 +- src/modules/tools/endstops/Endstops.cpp | 1 + src/modules/tools/endstops/Endstops.h | 6 +- src/modules/tools/laser/Laser.cpp | 12 +- src/modules/tools/laser/Laser.h | 10 +- src/modules/tools/switch/Switch.cpp | 14 +- 28 files changed, 535 insertions(+), 171 deletions(-) create mode 100644 src/libs/MemoryPool.cpp create mode 100644 src/libs/MemoryPool.h create mode 100644 src/libs/platform_memory.cpp create mode 100644 src/libs/platform_memory.h mode change 100755 => 100644 src/modules/robot/Block.h diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index c8967db0..83de6013 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -12,7 +12,7 @@ #include "libs/SlowTicker.h" #include "libs/Adc.h" #include "libs/StreamOutputPool.h" -//#include +#include #include "checksumm.h" #include "ConfigValue.h" @@ -72,6 +72,8 @@ Kernel::Kernel(){ // Pre-load the config cache, do after setting up serial so we can report errors to serial this->config->config_cache_load(); + // For slow repeteative tasks + this->add_module( this->slow_ticker = new SlowTicker()); // now config is loaded we can do normal setup for serial based on config //TOADDBACK when the USB libraries are ported // delete this->serial; @@ -120,15 +122,18 @@ Kernel::Kernel(){ this->add_module( this->serial ); + // Configure the step ticker + this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); + float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); + // REMOVE this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(1000)->as_number(); + // HAL stuff add_module( this->slow_ticker = new SlowTicker()); this->step_ticker = new StepTicker(); -//TODO ADDBACK this->adc = new Adc(); + this->adc = new Adc(); // TODO : These should go into platform-specific files - // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 -/* NVIC_SetPriorityGrouping(0); NVIC_SetPriority(TIMER0_IRQn, 2); NVIC_SetPriority(TIMER1_IRQn, 1); @@ -136,7 +141,10 @@ Kernel::Kernel(){ NVIC_SetPriority(PendSV_IRQn, 3); // Set other priorities lower than the timers - NVIC_SetPriority(ADC_IRQn, 5); + NVIC_SetPriority(ADC0_IRQn, 5); + NVIC_SetPriority(ADC1_IRQn, 5); + // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 +/* NVIC_SetPriority(USB_IRQn, 5); // If MRI is enabled @@ -152,9 +160,6 @@ Kernel::Kernel(){ NVIC_SetPriority(UART3_IRQn, 5); } */ - // Configure the step ticker - this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); - float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(1)->as_number(); // Configure the step ticker this->step_ticker->set_frequency( this->base_stepping_frequency ); @@ -249,7 +254,7 @@ void Kernel::call_event(_EVENT_ENUM id_event, void * argument){ bool was_idle= true; if(id_event == ON_HALT) { this->halted= (argument == nullptr); - was_idle= conveyor->is_queue_empty(); // see if we were doing anything like printing + was_idle= conveyor->is_idle(); // see if we were doing anything like printing } // send to all registered modules diff --git a/src/libs/Kernel.h b/src/libs/Kernel.h index 84934301..68c1f8d3 100644 --- a/src/libs/Kernel.h +++ b/src/libs/Kernel.h @@ -26,6 +26,7 @@ class SerialConsole; class StreamOutputPool; class GcodeDispatch; class Robot; +class Stepper; class Planner; class StepTicker; class Adc; @@ -85,6 +86,7 @@ class Kernel { bool feed_hold:1; bool ok_per_line:1; }; + }; #endif diff --git a/src/libs/MemoryPool.cpp b/src/libs/MemoryPool.cpp new file mode 100644 index 00000000..60474ec4 --- /dev/null +++ b/src/libs/MemoryPool.cpp @@ -0,0 +1,244 @@ +#include "MemoryPool.h" + +#include "StreamOutput.h" + +#include +#include + +// this catches all usages of delete blah. The object's destructor is called before we get here +// it first checks if the deleted object is part of a pool, and uses free otherwise. +void operator delete(void* p) +{ + MemoryPool* m = MemoryPool::first; + while (m) + { + if (m->has(p)) + { + MDEBUG("Pool %p has %p, using dealloc()\n", m, p); + m->dealloc(p); + return; + } + m = m->next; + } + + MDEBUG("no pool has %p, using free()\n", p); + free(p); +} + + +#define offset(x) (((uint8_t*) x) - ((uint8_t*) this->base)) + +typedef struct __attribute__ ((packed)) +{ + uint32_t next :31; + uint32_t used :1; + + uint8_t data[]; +} _poolregion; + +MemoryPool* MemoryPool::first = NULL; + +MemoryPool::MemoryPool(void* base, uint16_t size) +{ + this->base = base; + this->size = size; + + ((_poolregion*) base)->used = 0; + ((_poolregion*) base)->next = size; + + // insert ourselves into head of LL + next = first; + first = this; +} + +MemoryPool::~MemoryPool() +{ + MDEBUG("Pool %p destroyed: region %p (%d)\n", this, base, size); + + // remove ourselves from the LL + if (first == this) + { // special case: we're first + first = this->next; + return; + } + + // otherwise search the LL for the previous pool + MemoryPool* m = first; + while (m) + { + if (m->next == this) + { + m->next = next; + return; + } + m = m->next; + } +} + +void* MemoryPool::alloc(size_t nbytes) +{ + // nbytes = ceil(nbytes / 4) * 4 + if (nbytes & 3) + nbytes += 4 - (nbytes & 3); + + // start at the start + _poolregion* p = ((_poolregion*) base); + + // find the allocation size including our metadata + uint16_t nsize = nbytes + sizeof(_poolregion); + + MDEBUG("\tallocate %d bytes from %p\n", nsize, base); + + // now we walk the list, looking for a sufficiently large free block + do { + MDEBUG("\t\tchecking %p (%s, %db)\n", p, (p->used?"used":"free"), p->next); + if ((p->used == 0) && (p->next >= nsize)) + { // we found a free space that's big enough + MDEBUG("\t\tFOUND free block at %p (%+d) with %d bytes\n", p, offset(p), p->next); + // mark it as used + p->used = 1; + + // if there's free space at the end of this block + if (p->next > nsize) + { + // q = p->next + _poolregion* q = (_poolregion*) (((uint8_t*) p) + nsize); + + MDEBUG("\t\twriting header to %p (%+d) (%d)\n", q, offset(q), p->next - nsize); + // write a new block header into q + q->used = 0; + q->next = p->next - nsize; + + // set our next to point to it + p->next = nsize; + + // sanity check + if (offset(q) >= size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + } + + // then return the data region for the block + return &p->data; + } + + // p = p->next + p = (_poolregion*) (((uint8_t*) p) + p->next); + + // make sure we don't walk off the end + } while (p <= (_poolregion*) (((uint8_t*)base) + size)); + + // fell off the end of the region! + return NULL; +} + +void MemoryPool::dealloc(void* d) +{ + _poolregion* p = (_poolregion*) (((uint8_t*) d) - sizeof(_poolregion)); + p->used = 0; + + MDEBUG("\tdeallocating %p (%+d, %db)\n", p, offset(p), p->next); + + // combine next block if it's free + _poolregion* q = (_poolregion*) (((uint8_t*) p) + p->next); + if (q->used == 0) + { + MDEBUG("\t\tCombining with next free region at %p, new size is %d\n", q, p->next + q->next); + + // sanity check + if (offset(q) > size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + + p->next += q->next; + } + + // walk the list to find previous block + q = (_poolregion*) base; + do { + // check if q is the previous block + if ((((uint8_t*) q) + q->next) == (uint8_t*) p) { + // q is the previous block. + if (q->used == 0) + { // if q is free + MDEBUG("\t\tCombining with previous free region at %p, new size is %d\n", q, p->next + q->next); + + // combine! + q->next += p->next; + + // sanity check + if ((offset(p) + p->next) >= size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + } + + // we found previous block, return + return; + } + + // return if last block + if (offset(q) + q->next >= size) + return; + + // q = q->next + q = (_poolregion*) (((uint8_t*) q) + q->next); + + // if some idiot deallocates our memory region while we're using it, strange things can happen. + // avoid an infinite loop in that case, however we'll still leak memory and may corrupt things + if (q->next == 0) + return; + + // make sure we don't walk off the end + } while (q < (_poolregion*) (((uint8_t*) base) + size)); +} + +void MemoryPool::debug(StreamOutput* str) +{ + _poolregion* p = (_poolregion*) base; + uint32_t tot = 0; + uint32_t free = 0; + str->printf("Start: %ub MemoryPool at %p\n", size, p); + do { + str->printf("\tChunk at %p (%+4d): %s, %lu bytes\n", p, offset(p), (p->used?"used":"free"), p->next); + tot += p->next; + if (p->used == 0) + free += p->next; + if ((offset(p) + p->next >= size) || (p->next <= sizeof(_poolregion))) + { + str->printf("End: total %lub, free: %lub\n", tot, free); + return; + } + p = (_poolregion*) (((uint8_t*) p) + p->next); + } while (1); +} + +bool MemoryPool::has(void* p) +{ + return ((p >= base) && (p < (void*) (((uint8_t*) base) + size))); +} + +uint32_t MemoryPool::free() +{ + uint32_t free = 0; + + _poolregion* p = (_poolregion*) base; + + do { + if (p->used == 0) + free += p->next; + if (offset(p) + p->next >= size) + return free; + if (p->next <= sizeof(_poolregion)) + return free; + p = (_poolregion*) (((uint8_t*) p) + p->next); + } while (1); +} diff --git a/src/libs/MemoryPool.h b/src/libs/MemoryPool.h new file mode 100644 index 00000000..a2a97357 --- /dev/null +++ b/src/libs/MemoryPool.h @@ -0,0 +1,58 @@ +#ifndef _MEMORYPOOL_H +#define _MEMORYPOOL_H + +#include +// #include +#include + +#ifdef MEMDEBUG + #define MDEBUG(...) printf(__VA_ARGS__) +#else + #define MDEBUG(...) do {} while (0) +#endif + +class StreamOutput; + +/* + * with MUCH thanks to http://www.parashift.com/c++-faq-lite/memory-pools.html + * + * test framework at https://gist.github.com/triffid/5563987 + */ + +class MemoryPool +{ +public: + MemoryPool(void* base, uint16_t size); + ~MemoryPool(); + + void* alloc(size_t); + void dealloc(void* p); + + void debug(StreamOutput*); + + bool has(void*); + + uint32_t free(void); + + MemoryPool* next; + + static MemoryPool* first; + +private: + void* base; + uint16_t size; +}; + +// this overloads "placement new" +inline void* operator new(size_t nbytes, MemoryPool& pool) +{ + return pool.alloc(nbytes); +} + +// this allows placement new to free memory if the constructor fails +inline void operator delete(void* p, MemoryPool& pool) +{ + pool.dealloc(p); +} + +#endif /* _MEMORYPOOL_H */ diff --git a/src/libs/Pwm.cpp b/src/libs/Pwm.cpp index 744ee9e4..de30e9a0 100644 --- a/src/libs/Pwm.cpp +++ b/src/libs/Pwm.cpp @@ -1,6 +1,7 @@ #include "Pwm.h" #include "nuts_bolts.h" +#include "utils.h" #define PID_PWM_MAX 256 diff --git a/src/libs/RingBuffer.h b/src/libs/RingBuffer.h index 3ae9ec6b..24b25621 100644 --- a/src/libs/RingBuffer.h +++ b/src/libs/RingBuffer.h @@ -3,7 +3,6 @@ Smoothie 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. Smoothie 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 Smoothie. If not, see . - With chucks taken from http://en.wikipedia.org/wiki/Circular_buffer, see licence there also */ @@ -40,9 +39,9 @@ template int RingBuffer::capacity(){ } template int RingBuffer::size(){ - // TOADDBACK __disable_irq(); + __asm volatile ("cpsid i"); // equivalent to CMSIS '__disable_irq()' function int i = head - tail + ((tail > head)?length:0); - // TOADDBACK __enable_irq(); + __asm volatile ("cpsie i"); // equivalent to CMSIS '__enable_irq()' function return i; } diff --git a/src/libs/SlowTicker.cpp b/src/libs/SlowTicker.cpp index 90f36eee..8f895040 100644 --- a/src/libs/SlowTicker.cpp +++ b/src/libs/SlowTicker.cpp @@ -15,7 +15,6 @@ using namespace std; #include "libs/Hook.h" #include "modules/robot/Conveyor.h" #include "Gcode.h" -#include "mbed.h" #include @@ -26,17 +25,23 @@ SlowTicker* global_slow_ticker; SlowTicker::SlowTicker(){ global_slow_ticker = this; - //this->ticker = new Ticker(); // ISP button FIXME: WHy is this here? - ispbtn.from_string("2.10")->as_input()->pull_up(); + // TOADDBACK ispbtn.from_string("2.10")->as_input()->pull_up(); + + uint32_t PCLK = SystemCoreClock; + uint32_t prescale = PCLK / 1000000; //Increment MR each uSecond - //LPC_SC->PCONP |= (1 << 22); // Power Ticker ON - /* Enable timer 0 clock and reset it */ + /* Enable timer 1 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER2].CFG |= 1; - LPC_TIMER2->MCR = 3; // Match on MR0, reset on MR0 - // do not enable interrupt until setup is complete - LPC_TIMER2->TCR = 0; // Disable interrupt + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER2_RST & 31); //Trigger a peripheral reset for the timer + while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER2_RST & 31)))){} + /* Configure Timer 0 */ + LPC_TIMER2->CTCR = 0x0; // timer mode + LPC_TIMER2->TCR = 0; // Disable interrupt + LPC_TIMER2->PR = prescale - 1; + LPC_TIMER2->MR[0] = 10000000; // Initial dummy value for Match Register + LPC_TIMER2->MCR |= 3; // match on Mr0, stop on match max_frequency = 5; // initial max frequency is set to 5Hz set_frequency(max_frequency); @@ -55,12 +60,11 @@ void SlowTicker::on_module_loaded(){ // Set the base frequency we use for all sub-frequencies void SlowTicker::set_frequency( int frequency ){ - this->interval = (float)1 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second + this->interval = SystemCoreClock / frequency; // SystemCoreClock = Timer increments in a second LPC_TIMER2->MR[0] = this->interval; LPC_TIMER2->TCR = 3; // Reset LPC_TIMER2->TCR = 1; // Reset flag_1s_count= SystemCoreClock>>2; -// this->ticker->attach_us(this, &SlowTicker::tick,(int)( this->interval * (float)1000000) ); } // The actual interrupt being called by the timer, this is where work is done @@ -89,8 +93,10 @@ void SlowTicker::tick(){ // Enter MRI mode if the ISP button is pressed // TODO: This should have it's own module + /* TOADDBACK if (ispbtn.get() == 0) __debugbreak(); + */ } @@ -112,7 +118,7 @@ bool SlowTicker::flag_1s(){ __enable_irq(); return false; } - +#include "mbed.h" extern DigitalOut leds[]; void SlowTicker::on_idle(void*) { @@ -134,4 +140,3 @@ extern "C" void TIMER2_IRQHandler (void){ } global_slow_ticker->tick(); } - diff --git a/src/libs/SlowTicker.h b/src/libs/SlowTicker.h index 621fec41..08b4be6d 100644 --- a/src/libs/SlowTicker.h +++ b/src/libs/SlowTicker.h @@ -19,7 +19,6 @@ using namespace std; #include "libs/Pin.h" #include -#include "mbed.h" class SlowTicker : public Module{ public: @@ -34,7 +33,7 @@ class SlowTicker : public Module{ // TODO replace this with std::function() template Hook* attach( uint32_t frequency, T *optr, uint32_t ( T::*fptr )( uint32_t ) ){ Hook* hook = new Hook(); - hook->interval = ((float)1)/frequency; + hook->interval = floorf((SystemCoreClock)/frequency); hook->attach(optr, fptr); hook->countdown = hook->interval; @@ -55,7 +54,6 @@ class SlowTicker : public Module{ vector hooks; uint32_t max_frequency; uint32_t interval; - Ticker* ticker; Pin ispbtn; protected: diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 8a355efe..2b9544db 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -16,7 +16,6 @@ #include "Block.h" #include "Conveyor.h" -//#include "system_LPC17xx.h" // mbed.h lib #include #include @@ -32,18 +31,23 @@ GPIO stepticker_debug_pin(STEPTICKER_DEBUG_PIN); #define SET_STEPTICKER_DEBUG_PIN(n) #endif + +// StepTicker handles the base frequency ticking for the Stepper Motors / Actuators +// It has a list of those, and calls their tick() functions at regular intervals +// They then do Bresenham stuff themselves + StepTicker *StepTicker::instance; -StepTicker::StepTicker() -{ +StepTicker::StepTicker(){ + instance = this; // setup the Singleton instance of the stepticker uint32_t PCLK = SystemCoreClock; - uint32_t prescale = PCLK / 10000000; + uint32_t prescale = PCLK / 1000000; //Increment MR each uSecond /* Enable timer 0 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER0].CFG |= 1; - LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER0_RST & 31); //Trigger a peripheral reset for the timer + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER0_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER0_RST & 31)))){} /* Configure Timer 0 */ LPC_TIMER0->CTCR = 0x0; // timer mode @@ -51,13 +55,11 @@ StepTicker::StepTicker() LPC_TIMER0->PR = prescale - 1; LPC_TIMER0->MR[0] = 10000000; // Initial dummy value for Match Register LPC_TIMER0->MCR |= 3; // Match on MR0, reset on MR0 - NVIC_SetPriority(TIMER0_IRQn,0); /* Enable timer 1 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER1].CFG |= 1; - LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER1_RST & 31)))){} - /* Configure Timer 1 */ LPC_TIMER1->CTCR = 0x0; // timer mode LPC_TIMER1->TCR = 0; // Disable interrupt @@ -65,9 +67,10 @@ StepTicker::StepTicker() LPC_TIMER1->MR[0] = 10000000; // Initial dummy value for Match Register LPC_TIMER1->MCR |= 5; // match on Mr0, stop on match + // Default start values this->set_frequency(100000); - this->set_unstep_time(100); + this->set_unstep_time(10); this->unstep.reset(); this->num_motors = 0; @@ -75,11 +78,12 @@ StepTicker::StepTicker() this->running = false; this->current_block = nullptr; - #ifdef STEPTICKER_DEBUG_PIN +#ifdef STEPTICKER_DEBUG_PIN // setup debug pin if defined stepticker_debug_pin.output(); stepticker_debug_pin= 0; - #endif +#endif + } StepTicker::~StepTicker() @@ -98,19 +102,18 @@ void StepTicker::start() void StepTicker::set_frequency( float frequency ) { this->frequency = frequency; - this->period_us = (int)lrint(1000000/frequency); + this->period = (int)lrint(1000000/frequency); - /* Update Timer 0 Match register and reset timer */ - LPC_TIMER0->MR[0] = this->period_us; - LPC_TIMER0->TCR = 3; // Reset - LPC_TIMER0->TCR = 1; // start + LPC_TIMER0->MR[0] = this->period; + LPC_TIMER0->TCR = 3; // Reset + LPC_TIMER0->TCR = 1; // start } -// Set the reset delay -void StepTicker::set_unstep_time( float microseconds ){ - this->reset_delay_us = (int)lrint(microseconds); - /* Update Timer 1 Match register */ - LPC_TIMER1->MR[0] = this->reset_delay_us; +// Set the reset delay, must be called after set_frequency +void StepTicker::set_unstep_time( float microseconds ) +{ + uint32_t delay = (int)lrint(microseconds); + LPC_TIMER1->MR[0] = delay; // TODO check that the unstep time is less than the step period, if not slow down step ticker } @@ -126,27 +129,31 @@ void StepTicker::unstep_tick() this->unstep.reset(); } - // Unstep timer interrupt handler extern "C" void TIMER1_IRQHandler (void) { // SEGGER_RTT_LOCK(); // SEGGER_SYSVIEW_RecordEnterISR(); - LPC_TIMER1->IR |= 1 << 0; - StepTicker::getInstance()->unstep_tick(); - NVIC_ClearPendingIRQ(TIMER1_IRQn); +// + LPC_TIMER1->IR |= 1 << 0; + StepTicker::getInstance()->unstep_tick(); + +// NVIC_ClearPendingIRQ(TIMER1_IRQn); // SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } -// Step timer interrupt handler +// The actual interrupt handler where we do all the work extern "C" void TIMER0_IRQHandler (void) { -// SEGGER_RTT_LOCK(); -// SEGGER_SYSVIEW_RecordEnterISR(); - LPC_TIMER0->IR |= 1 << 0; - StepTicker::getInstance()->step_tick(); - NVIC_ClearPendingIRQ(TIMER0_IRQn); +// SEGGER_RTT_LOCK(); +// SEGGER_SYSVIEW_RecordEnterISR(); +// + // Reset interrupt register + LPC_TIMER0->IR |= 1 << 0; + StepTicker::getInstance()->step_tick(); + +// NVIC_ClearPendingIRQ(TIMER0_IRQn); // SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } @@ -245,10 +252,9 @@ void StepTicker::step_tick (void) // Note there could be a race here if we run another tick before the unsteps have happened, // right now it takes about 3-4us but if the unstep were near 10uS or greater it would be an issue // also it takes at least 2us to get here so even when set to 1us pulse width it will still be about 3us - if( this->unstep.any()){ - /* Start Unstep timer (Timer 1) */ - LPC_TIMER1->TCR = 0x3; // reset - LPC_TIMER1->TCR = 1; // enable = 1, reset = 0 + if( unstep.any()) { + LPC_TIMER1->TCR = 3; + LPC_TIMER1->TCR = 1; } @@ -318,3 +324,4 @@ int StepTicker::register_motor(StepperMotor* m) motor[num_motors++] = m; return num_motors - 1; } + diff --git a/src/libs/StepTicker.h b/src/libs/StepTicker.h index 63e443f7..46590e08 100644 --- a/src/libs/StepTicker.h +++ b/src/libs/StepTicker.h @@ -7,10 +7,11 @@ -#pragma once +#ifndef STEPTICKER_H +#define STEPTICKER_H #include -#include +#include #include #include #include @@ -46,10 +47,6 @@ class StepTicker{ static StepTicker *getInstance() { return instance; } - // TOADDBACK was private - volatile uint32_t tick_cnt; - uint32_t period_us; - uint32_t reset_delay_us; private: static StepTicker *instance; @@ -68,3 +65,6 @@ class StepTicker{ uint8_t num_motors:4; }; }; + + +#endif diff --git a/src/libs/StepperMotor.cpp b/src/libs/StepperMotor.cpp index f2183cee..799af7dd 100644 --- a/src/libs/StepperMotor.cpp +++ b/src/libs/StepperMotor.cpp @@ -15,7 +15,7 @@ StepperMotor::StepperMotor(Pin &step, Pin &dir, Pin &en) : step_pin(step), dir_pin(dir), en_pin(en) { - //TODO ADDBACK set_high_on_debug(en.port_number, en.pin_number); + //TOADDBACK set_high_on_debug(en.port_number, en.pin); steps_per_mm = 1.0F; max_rate = 50.0F; diff --git a/src/libs/StepperMotor.h b/src/libs/StepperMotor.h index 5b017fb7..af9d245f 100644 --- a/src/libs/StepperMotor.h +++ b/src/libs/StepperMotor.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef STEPPERMOTOR_H +#define STEPPERMOTOR_H #include "Module.h" #include "Pin.h" @@ -79,3 +80,7 @@ class StepperMotor : public Module { bool selected:1; }; }; + + +#endif + diff --git a/src/libs/nuts_bolts.h b/src/libs/nuts_bolts.h index 5f799586..f17f4e99 100644 --- a/src/libs/nuts_bolts.h +++ b/src/libs/nuts_bolts.h @@ -1,19 +1,15 @@ /* nuts_bolts.h - cartesian robot controller. 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 . */ diff --git a/src/libs/platform_memory.cpp b/src/libs/platform_memory.cpp new file mode 100644 index 00000000..f80b287c --- /dev/null +++ b/src/libs/platform_memory.cpp @@ -0,0 +1,4 @@ +#include "platform_memory.h" + +MemoryPool* _AHB0; +MemoryPool* _AHB1; diff --git a/src/libs/platform_memory.h b/src/libs/platform_memory.h new file mode 100644 index 00000000..71ffe2c0 --- /dev/null +++ b/src/libs/platform_memory.h @@ -0,0 +1,12 @@ +#ifndef _PLATFORM_MEMORY_H +#define _PLATFORM_MEMORY_H + +#include "MemoryPool.h" + +#define AHB0 (*_AHB0) +#define AHB1 (*_AHB1) + +extern MemoryPool* _AHB0; +extern MemoryPool* _AHB1; + +#endif /* _PLATFORM_MEMORY_H */ diff --git a/src/libs/utils.cpp b/src/libs/utils.cpp index 9bb9c8ac..273a5991 100644 --- a/src/libs/utils.cpp +++ b/src/libs/utils.cpp @@ -263,18 +263,13 @@ string wcs2gcode(int wcs) { return str; } -void safe_delay_ms(uint32_t delay) +void safe_delay(uint32_t delayms) { - safe_delay_us(delay*1000); -} - -void safe_delay_us(uint32_t dus) -{ - uint32_t start = us_ticker_read(); - while ((us_ticker_read() - start) < dus) { + uint32_t dus= delayms*1000; //convert ms to us + uint32_t counter = 0; + while( counter < dus ){ THEKERNEL->call_event(ON_IDLE); -//TODO determine if below code is still necessary -// counter++; -// wait_us(1); + counter++; + wait_us(1); } } diff --git a/src/libs/utils.h b/src/libs/utils.h index d8aa7eb8..4cb3025e 100644 --- a/src/libs/utils.h +++ b/src/libs/utils.h @@ -1,10 +1,14 @@ -#pragma once +#ifndef UTILS_H +#define UTILS_H #include #include #include -std::string lc(const std::string& str); +using std::string; +using std::vector; + +string lc(const string& str); bool is_alpha( int ); bool is_digit( int ); @@ -12,32 +16,27 @@ bool is_numeric( int ); bool is_alphanum( int ); bool is_whitespace( int ); -std::vector split(const char *str, char c = ','); -std::vector parse_number_list(const char *str); -std::vector parse_number_list(const char *str, uint8_t radix); +vector split(const char *str, char c = ','); +vector parse_number_list(const char *str); +vector parse_number_list(const char *str, uint8_t radix); -std::string remove_non_number( std::string str ); +string remove_non_number( string str ); -uint16_t get_checksum(const std::string& to_check); +uint16_t get_checksum(const string& to_check); uint16_t get_checksum(const char* to_check); -void get_checksums(uint16_t check_sums[], const std::string& key); - -std::string shift_parameter( std::string ¶meters ); +void get_checksums(uint16_t check_sums[], const string& key); -std::string get_arguments( const std::string& possible_command ); +string shift_parameter( string ¶meters ); -bool file_exists( const std::string file_name ); +string get_arguments( const string& possible_command ); -void system_reset( bool dfu= false ); +bool file_exists( const string file_name ); -std::string absolute_from_relative( std::string path ); +string absolute_from_relative( string path ); int append_parameters(char *buf, std::vector> params, size_t bufsize); -std::string wcs2gcode(int wcs); -void safe_delay_us(uint32_t delay); -void safe_delay_ms(uint32_t delay); - -#define confine(value, min, max) (((value) < (min))?(min):(((value) > (max))?(max):(value))) +string wcs2gcode(int wcs); +void safe_delay(uint32_t delay); -//#define dd(...) LPC_GPIO2->FIODIR = 0xffff; LPC_GPIO2->FIOCLR = 0xffff; LPC_GPIO2->FIOSET = __VA_ARGS__ +#endif diff --git a/src/main.cpp b/src/main.cpp index 99b385ea..263b1830 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,18 +43,21 @@ // Debug #include "libs/SerialMessage.h" - -//#include "libs/USBDevice/USB.h" -//#include "libs/USBDevice/USBMSD/USBMSD.h" -//#include "libs/USBDevice/USBMSD/SDCard.h" -//#include "libs/USBDevice/USBSerial/USBSerial.h" -//#include "libs/USBDevice/DFU.h" -//#include "libs/SDFAT.h" +/* +#include "libs/USBDevice/USB.h" +#include "libs/USBDevice/USBMSD/USBMSD.h" +#include "libs/USBDevice/USBMSD/SDCard.h" +#include "libs/USBDevice/USBSerial/USBSerial.h" +#include "libs/USBDevice/DFU.h" +#include "libs/SDFAT.h" +*/ #include "StreamOutputPool.h" #include "ToolManager.h" #include "SEGGER_SYSVIEW.h" #include "version.h" +#include "system_LPC43xx.h" +//#include "platform_memory.h" #include "mbed.h" @@ -83,14 +86,23 @@ DigitalOut leds[4] = { DigitalOut(P2_8), DigitalOut(P2_9) #endif - }; +/* +USB u __attribute__ ((section ("AHBSRAM0"))); +USBSerial usbserial __attribute__ ((section ("AHBSRAM0"))) (&u); +#ifndef DISABLEMSD +USBMSD msc __attribute__ ((section ("AHBSRAM0"))) (&u, &sd); +#else +USBMSD *msc= NULL; +#endif +*/ + void init() { SEGGER_SYSVIEW_Conf(); - // Kernel creates modules, and receives and dispatches events between them - Kernel* kernel = new Kernel(); + // Kernel creates modules, and receives and dispatches events between them + Kernel* kernel = new Kernel(); kernel->streams->printf("Smoothie Running @%ldMHz\r\n", SystemCoreClock / 1000000); Version version; @@ -98,28 +110,28 @@ void init() { #ifdef CNC kernel->streams->printf(" CNC Build\r\n"); #endif +#ifdef DISABLEMSD + kernel->streams->printf(" NOMSD Build\r\n"); +#endif bool sdok= false; //TODO remove once SD Card code is working // bool sdok= (sd.disk_initialize() == 0); // if(!sdok) kernel->streams->printf("SDCard failed to initialize\r\n"); - #ifdef NONETWORK - kernel->streams->printf("NETWORK is disabled\r\n"); - #endif -//#ifdef DISABLEMSD -// // attempt to be able to disable msd in config -// if(sdok && !kernel->config->value( disable_msd_checksum )->by_default(false)->as_bool()){ -// // HACK to zero the memory USBMSD uses as it and its objects seem to not initialize properly in the ctor -// size_t n= sizeof(USBMSD); -// void *v = AHB0.alloc(n); -// memset(v, 0, n); // clear the allocated memory -// msc= new(v) USBMSD(&u, &sd); // allocate object using zeroed memory -// }else{ -// msc= NULL; -// kernel->streams->printf("MSD is disabled\r\n"); -// } -//#endif +#ifdef DISABLEMSD + // attempt to be able to disable msd in config + if(sdok && !kernel->config->value( disable_msd_checksum )->by_default(false)->as_bool()){ + // HACK to zero the memory USBMSD uses as it and its objects seem to not initialize properly in the ctor + size_t n= sizeof(USBMSD); + void *v = AHB0.alloc(n); + memset(v, 0, n); // clear the allocated memory + msc= new(v) USBMSD(&u, &sd); // allocate object using zeroed memory + }else{ + msc= NULL; + kernel->streams->printf("MSD is disabled\r\n"); + } +#endif // Create and add main modules // kernel->add_module( new Player() ); @@ -134,6 +146,7 @@ bool sdok= false; //TODO remove once SD Card code is working #endif #ifndef NO_TOOLS_SWITCH + // Create all Switch modules SwitchPool *sp= new SwitchPool(); sp->load_tools(); delete sp; @@ -220,12 +233,12 @@ bool sdok= false; //TODO remove once SD Card code is working // memory before cache is cleared //SimpleShell::print_mem(kernel->streams); - // clear up the config cache to save some memory + // Clear the configuration cache as it is no longer needed kernel->config->config_cache_clear(); if(kernel->is_using_leds()) { // set some leds to indicate status... led0 init done, led1 mainloop running, led2 idle loop running, led3 sdcard ok - leds[0]= 1; // indicate we are done with init + leds[0]= 0; // indicate we are done with init leds[3]= sdok?1:0; // 4th led indicates sdcard is available (TODO maye should indicate config was found) } @@ -248,23 +261,26 @@ bool sdok= false; //TODO remove once SD Card code is working } // start the timers and interrupts - THEKERNEL->conveyor->start(THEROBOT->get_number_registered_motors()); - THEKERNEL->step_ticker->start(); + kernel->conveyor->start(THEROBOT->get_number_registered_motors()); + kernel->step_ticker->start(); THEKERNEL->slow_ticker->start(); } -int main() -{ +int main() { + init(); - int cnt = 0; + + uint16_t cnt = 0; + // Main loop while(1){ if(THEKERNEL->is_using_leds()) { // flash led 2 to show we are alive - leds[0]= (cnt++ & 0x1000) ? 1 : 0; + leds[1]= (cnt++ & 0x1000) ? 1 : 0; } THEKERNEL->call_event(ON_MAIN_LOOP); THEKERNEL->call_event(ON_IDLE); } } + diff --git a/src/makefile b/src/makefile index 404fbce1..f4dcd9fb 100644 --- a/src/makefile +++ b/src/makefile @@ -14,6 +14,7 @@ MRI_BREAK_ON_INIT ?= 1 # slowly. It may run so slowly that it fails to execute properly since some routines (ie. step tickers) are # time sensitive. #OPTIMIZATION := 0 +OPTIMIZATION := 2 # Set to non zero value if you want checks to be enabled which reserve a # specific amount of space for the stack. The heap's growth will be @@ -35,7 +36,7 @@ NO_FLOAT_SCANF := 1 NO_FLOAT_PRINTF := 0 LIBS_PREFIX := configdefault.o MRI_SEMIHOST_STDIO := 0 -MBED_LIBS += USBHost +#MBED_LIBS += USBHost #MBED_LIBS += USBDevice # Use C++11 features for the checksums @@ -83,14 +84,6 @@ DEFINES += -DTARGET_BAMBINO200E MRI_UART ?= MRI_UART_TX_P5_6 MRI_UART_RX_P1_14 MRI_UART_BAUD=230400 endif -# Disable the secondary console if the MRI debug monitor is enabled so that the secondary UART can be used by -# MRI instead. -ifeq "$(MRI_ENABLE)" "1" -DEFINES := $(subst -DSMOOTHIE_UART_SECONDARY_ENABLE=1,-DSMOOTHIE_UART_SECONDARY_ENABLE=0,$(DEFINES)) -GCC4MBED_TYPE := Checked -else -GCC4MBED_TYPE := Release -endif # this is the default UART baud rate used if it is not set in config # it is also the baud rate used to report any errors found while parsing the config file @@ -103,6 +96,16 @@ else DEFINES += -D__GITVERSIONSTRING__=\"$(shell generate-version.bat)\" endif +# Disable the secondary console if the MRI debug monitor is enabled so that the secondary UART can be used by +# MRI instead. +ifeq "$(MRI_ENABLE)" "1" +DEFINES := $(subst -DSMOOTHIE_UART_SECONDARY_ENABLE=1,-DSMOOTHIE_UART_SECONDARY_ENABLE=0,$(DEFINES)) +GCC4MBED_TYPE := Checked +else +GCC4MBED_TYPE := Release +endif + + include $(GCC4MBED_DIR)/build/gcc4mbed.mk # set to not compile in any network support diff --git a/src/modules/robot/ActuatorCoordinates.h b/src/modules/robot/ActuatorCoordinates.h index 03c0e82b..737f71aa 100755 --- a/src/modules/robot/ActuatorCoordinates.h +++ b/src/modules/robot/ActuatorCoordinates.h @@ -18,6 +18,7 @@ #endif #endif -// Keep MAX_ROBOT_ACTUATORS as small as practical it impacts block size and therefore free memory. +//The subset in use is determined by the arm solution's get_actuator_count(). +//Keep MAX_ROBOT_ACTUATORS as small as practical it impacts block size and therefore free memory. const size_t k_max_actuators = MAX_ROBOT_ACTUATORS; typedef struct std::array ActuatorCoordinates; diff --git a/src/modules/robot/Block.h b/src/modules/robot/Block.h old mode 100755 new mode 100644 index 3df796ce..da5c1996 --- a/src/modules/robot/Block.h +++ b/src/modules/robot/Block.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef BLOCK_H +#define BLOCK_H #include #include @@ -77,3 +78,6 @@ class Block { uint16_t s_value:12; // for laser 1.11 Fixed point }; }; + + +#endif diff --git a/src/modules/robot/Conveyor.h b/src/modules/robot/Conveyor.h index ba2b09a7..9ea8e40a 100644 --- a/src/modules/robot/Conveyor.h +++ b/src/modules/robot/Conveyor.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef CONVEYOR_H +#define CONVEYOR_H #include "libs/Module.h" #include "HeapRing.h" @@ -63,3 +64,6 @@ class Conveyor : public Module }; }; + + +#endif // CONVEYOR_H diff --git a/src/modules/robot/Planner.cpp b/src/modules/robot/Planner.cpp index 1432e44a..09ef0a83 100644 --- a/src/modules/robot/Planner.cpp +++ b/src/modules/robot/Planner.cpp @@ -53,7 +53,8 @@ void Planner::config_load() bool Planner::append_block( ActuatorCoordinates &actuator_pos, uint8_t n_motors, float rate_mm_s, float distance, float *unit_vec, float acceleration, float s_value, bool g123) { // Create ( recycle ) a new block - Block* block = THECONVEYOR->queue.head_ref(); + //Block* block = THECONVEYOR->queue.head_ref(); + Block* block = THEKERNEL->conveyor->queue.head_ref(); // Direction bits bool has_steps = false; diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index c36611bc..2064f78a 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -414,6 +414,7 @@ uint32_t Endstops::read_endstops(uint32_t dummy) // we signal the motor to stop, which will preempt any moves on that axis STEPPER[m]->stop_moving(); } + } else { // The endstop was not hit yet debounce[m] = 0; diff --git a/src/modules/tools/endstops/Endstops.h b/src/modules/tools/endstops/Endstops.h index 387781be..2089b983 100644 --- a/src/modules/tools/endstops/Endstops.h +++ b/src/modules/tools/endstops/Endstops.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef ENDSTOPS_MODULE_H +#define ENDSTOPS_MODULE_H #include "libs/Module.h" #include "libs/Pin.h" @@ -66,3 +67,6 @@ class Endstops : public Module{ bool move_to_origin_after_home:1; }; }; + + +#endif diff --git a/src/modules/tools/laser/Laser.cpp b/src/modules/tools/laser/Laser.cpp index 796338fb..04decc74 100644 --- a/src/modules/tools/laser/Laser.cpp +++ b/src/modules/tools/laser/Laser.cpp @@ -46,8 +46,9 @@ Laser::Laser() manual_fire= false; } -void Laser::on_module_loaded() { - if( !THEKERNEL->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ){ +void Laser::on_module_loaded() +{ + if( !THEKERNEL->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ) { // as not needed free up resource delete this; return; @@ -63,8 +64,7 @@ void Laser::on_module_loaded() { pwm_pin = dummy_pin->hardware_pwm(); - if (pwm_pin == nullptr) - { + if (pwm_pin == NULL) { // TOADDBACK THEKERNEL->streams->printf("Error: Laser cannot use P%d.%d (P2.0 - P2.5, P1.18, P1.20, P1.21, P1.23, P1.24, P1.26, P3.25, P3.26 only). Laser module disabled.\n", dummy_pin->port_number, dummy_pin->pin); delete dummy_pin; delete this; @@ -107,10 +107,6 @@ void Laser::on_module_loaded() { set_laser_power(0); //register for events - this->register_for_event(ON_GCODE_EXECUTE); - this->register_for_event(ON_SPEED_CHANGE); - this->register_for_event(ON_BLOCK_BEGIN); - this->register_for_event(ON_BLOCK_END); this->register_for_event(ON_HALT); this->register_for_event(ON_GCODE_RECEIVED); this->register_for_event(ON_CONSOLE_LINE_RECEIVED); diff --git a/src/modules/tools/laser/Laser.h b/src/modules/tools/laser/Laser.h index 7978e291..49f6df2e 100644 --- a/src/modules/tools/laser/Laser.h +++ b/src/modules/tools/laser/Laser.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef LASER_MODULE_H +#define LASER_MODULE_H #include "libs/Module.h" @@ -38,7 +39,7 @@ class Laser : public Module{ float current_speed_ratio(const Block *block) const; mbed::PwmOut *pwm_pin; // PWM output to regulate the laser power - Pin *ttl_pin; // TTL output to fire laser + Pin *ttl_pin; // TTL output to fire laser float laser_maximum_power; // maximum allowed laser power to be output on the pwm pin float laser_minimum_power; // value used to tickle the laser on moves. Also minimum value for auto-scaling float laser_maximum_s_value; // Value of S code that will represent max power @@ -46,8 +47,11 @@ class Laser : public Module{ struct { bool laser_on:1; // set if the laser is on bool pwm_inverting:1; // stores whether the PWM period should be inverted - bool ttl_used:1; // stores whether we have a TTL output + bool ttl_used:1; // stores whether we have a TTL output bool ttl_inverting:1; // stores whether the TTL output should be inverted bool manual_fire:1; // set when manually firing }; }; + + +#endif diff --git a/src/modules/tools/switch/Switch.cpp b/src/modules/tools/switch/Switch.cpp index 0b27f8a6..1957562e 100644 --- a/src/modules/tools/switch/Switch.cpp +++ b/src/modules/tools/switch/Switch.cpp @@ -24,7 +24,7 @@ #include "PwmOut.h" -//TODO ADDBACK #include "MRI_Hooks.h" +// TOADDBACK #include "MRI_Hooks.h" #include @@ -106,9 +106,9 @@ void Switch::on_config_reload(void *argument) this->sigmadelta_pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); if(this->sigmadelta_pin->connected()) { if(failsafe == 1) { - //TODO ADDBACK set_high_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); + // TOADDBACKĀ set_high_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); }else{ - //TODO ADDBACK set_low_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); + // TOADDBACK set_low_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); } }else{ this->output_type= NONE; @@ -122,9 +122,9 @@ void Switch::on_config_reload(void *argument) this->digital_pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); if(this->digital_pin->connected()) { if(failsafe == 1) { - //TODO ADDBACK set_high_on_debug(digital_pin->port_number, digital_pin->pin); + // TOADDBACKĀ set_high_on_debug(digital_pin->port_number, digital_pin->pin); }else{ - //TODO ADDBACK set_low_on_debug(digital_pin->port_number, digital_pin->pin); + // TOADDBACK set_low_on_debug(digital_pin->port_number, digital_pin->pin); } }else{ this->output_type= NONE; @@ -138,9 +138,9 @@ void Switch::on_config_reload(void *argument) pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); this->pwm_pin= pin->hardware_pwm(); if(failsafe == 1) { - //TODO ADDBACK set_high_on_debug(pin->port_number, pin->pin); + // TOADDBACKĀ set_high_on_debug(pin->port_number, pin->pin); }else{ - //TODO ADDBACK set_low_on_debug(pin->port_number, pin->pin); + // TOADDBACK set_low_on_debug(pin->port_number, pin->pin); } delete pin; if(this->pwm_pin == nullptr) { From d113d11956c20dd06e0a94d9a268a7ee51091049 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 21:59:51 +1300 Subject: [PATCH 50/66] modified: src/config.default modified: src/modules/tools/spindle/Spindle.cpp --- src/config.default | 28 +++++++++++++-------------- src/modules/tools/spindle/Spindle.cpp | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/config.default b/src/config.default index f73ff3c8..82053203 100644 --- a/src/config.default +++ b/src/config.default @@ -31,12 +31,12 @@ junction_deviation 0.05 # Similar to the o # Stepper module configuration microseconds_per_step_pulse 5 # Duration of step pulses to stepper drivers, in microseconds -base_stepping_frequency 150000 #100000 # Base frequency for stepping +base_stepping_frequency 100000 # Base frequency for stepping # Cartesian axis speed limits -x_axis_max_speed 30000 # mm/min -y_axis_max_speed 30000 # mm/min -z_axis_max_speed 30000 # mm/min +x_axis_max_speed 60000 # mm/min +y_axis_max_speed 60000 # mm/min +z_axis_max_speed 60000 # mm/min # Arm solution configuration : Rotatable Delta robot. Translates mm positions into stepper positions arm_solution rotary_delta # selects the delta arm solution @@ -87,19 +87,19 @@ alpha_step_pin 5.0 # Pin for alpha st alpha_dir_pin 6.12 # Pin for alpha stepper direction alpha_en_pin 4.10 # Pin for alpha enable pin alpha_current 1.5 # X stepper motor current -alpha_max_rate 30000.0 # mm/min +alpha_max_rate 60000.0 # mm/min beta_step_pin 5.7 # Pin for beta stepper step signal beta_dir_pin 5.5 # Pin for beta stepper direction beta_en_pin 7.6 # Pin for beta enable beta_current 1.5 # Y stepper motor current -beta_max_rate 30000.0 # mm/min +beta_max_rate 60000.0 # mm/min gamma_step_pin 4.0 # Pin for gamma stepper step signal gamma_dir_pin 1.7 # Pin for gamma stepper direction gamma_en_pin 6.9 # Pin for gamma enable gamma_current 1.5 # Z stepper motor current -gamma_max_rate 30000.0 # mm/min +gamma_max_rate 60000.0 # mm/min #SilentStepStick TMC2130 setup motor_driver_control.alpha.enable true # alpha (X) is a TMC2130 @@ -258,15 +258,15 @@ rdelta_homing true # forces all three alpha_min_endstop 7.4!^ # alpha_max_endstop 7.7^ # add ! to invert pullup if switch is NO to ground alpha_homing_direction home_to_max # Home up -alpha_max 3000 # +alpha_max 300 # beta_min_endstop 7.5!^ # beta_max_endstop 4.3^ # beta_homing_direction home_to_max # -beta_max 30000 # +beta_max 300 # gamma_min_endstop 4.1!^ # gamma_max_endstop 4.4^ # gamma_homing_direction home_to_max # -gamma_max 30000 # +gamma_max 300 # # optional enable limit switches, actions will stop if any enabled limit switch is triggered alpha_limit_enable false # set to true to enable X min and max limit switches @@ -276,15 +276,15 @@ gamma_limit_enable false # set to true to en alpha_fast_homing_rate_mm_s 500 # feedrates in mm/second beta_fast_homing_rate_mm_s 500 # " gamma_fast_homing_rate_mm_s 500 # " -alpha_slow_homing_rate_mm_s 20 # " -beta_slow_homing_rate_mm_s 20 # " -gamma_slow_homing_rate_mm_s 20 # " +alpha_slow_homing_rate_mm_s 500 # " +beta_slow_homing_rate_mm_s 500 # " +gamma_slow_homing_rate_mm_s 500 # " alpha_homing_retract_mm 5 # distance in mm beta_homing_retract_mm 5 # " gamma_homing_retract_mm 5 # " -endstop_debounce_count 10 # uncomment if you get noise on your endstops, default is 100 +endstop_debounce_count 100 # uncomment if you get noise on your endstops, default is 100 alpha_trim 0 # software trim for alpha stepper endstop (in mm) beta_trim 0 # software trim for beta stepper endstop (in mm) diff --git a/src/modules/tools/spindle/Spindle.cpp b/src/modules/tools/spindle/Spindle.cpp index 879b8eec..9c2633f5 100644 --- a/src/modules/tools/spindle/Spindle.cpp +++ b/src/modules/tools/spindle/Spindle.cpp @@ -16,7 +16,7 @@ #include "SlowTicker.h" #include "Conveyor.h" //#include "system_LPC17xx.h" -#include "utils.h" +#include "nuts_bolts.h" #include "libs/Pin.h" #include "mbed.h" From e59556856529f351f566101225c118e90b83a3e0 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 22:07:39 +1300 Subject: [PATCH 51/66] modified: src/modules/tools/endstops/Endstops.cpp --- src/modules/tools/endstops/Endstops.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index 3cc92c48..2064f78a 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -835,7 +835,7 @@ void Endstops::on_gcode_received(void *argument) } break; -//TOADDBACK +//TODO ADDBACK /* // NOTE this is to test accuracy of lead screws etc. case 1910: { From 77b5e49bbab0689b93bb1d0d180c5f8fdb1d4612 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 22:08:53 +1300 Subject: [PATCH 52/66] modified: src/modules/tools/endstops/Endstops.cpp --- src/modules/tools/endstops/Endstops.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index 2064f78a..3cc92c48 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -835,7 +835,7 @@ void Endstops::on_gcode_received(void *argument) } break; -//TODO ADDBACK +//TOADDBACK /* // NOTE this is to test accuracy of lead screws etc. case 1910: { From edfd8f1be8a7a7755af914731afb1a625d91320f Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 22:15:29 +1300 Subject: [PATCH 53/66] modified: src/Segger_SystemView/README.md --- src/Segger_SystemView/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md index f9aaf56b..72e5bd73 100644 --- a/src/Segger_SystemView/README.md +++ b/src/Segger_SystemView/README.md @@ -1,7 +1,12 @@ #Segger JTrace SystemView debugging and integration +<<<<<<< 77b5e49bbab0689b93bb1d0d180c5f8fdb1d4612 Go to http://www.segger.com and download their SystemView free tool and copy only some of the source code files so you end up with the following files: ``` +======= +Go to http://www.segger.com and download their Systemview free tool and place the source code in this folder, you should end up with the following files: + +>>>>>>> deleted: src/Segger_SystemView/Global.h src/Segger_SystemView/SEGGER_SYSVIEW_Int.h src/Segger_SystemView/Global.h src/Segger_SystemView/SEGGER.h @@ -13,6 +18,7 @@ src/Segger_SystemView/SEGGER_SYSVIEW.h src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c +<<<<<<< 77b5e49bbab0689b93bb1d0d180c5f8fdb1d4612 ``` You will need to make the following changes in src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c @@ -50,3 +56,5 @@ JLinkGDBServer -if jtag -device LPC4330 -speed 15000 Note: I run my JLinkGDBServer on a separate Ubuntu machine onto which my Firepick is attached and use TCP/IP to connect to it. +======= +>>>>>>> deleted: src/Segger_SystemView/Global.h From 25b5f30c5ad0e33590e467d8bb5df35f9cb80cdd Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 22:18:35 +1300 Subject: [PATCH 54/66] modified: src/Segger_SystemView/README.md --- src/Segger_SystemView/README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Segger_SystemView/README.md b/src/Segger_SystemView/README.md index 72e5bd73..1d0390a1 100644 --- a/src/Segger_SystemView/README.md +++ b/src/Segger_SystemView/README.md @@ -1,12 +1,7 @@ #Segger JTrace SystemView debugging and integration -<<<<<<< 77b5e49bbab0689b93bb1d0d180c5f8fdb1d4612 Go to http://www.segger.com and download their SystemView free tool and copy only some of the source code files so you end up with the following files: ``` -======= -Go to http://www.segger.com and download their Systemview free tool and place the source code in this folder, you should end up with the following files: - ->>>>>>> deleted: src/Segger_SystemView/Global.h src/Segger_SystemView/SEGGER_SYSVIEW_Int.h src/Segger_SystemView/Global.h src/Segger_SystemView/SEGGER.h @@ -18,7 +13,6 @@ src/Segger_SystemView/SEGGER_SYSVIEW.h src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c -<<<<<<< 77b5e49bbab0689b93bb1d0d180c5f8fdb1d4612 ``` You will need to make the following changes in src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c @@ -54,7 +48,3 @@ Note: 4096 is the largest buffer size supported; you can also increase the speed JLinkGDBServer -if jtag -device LPC4330 -speed 15000 Note: I run my JLinkGDBServer on a separate Ubuntu machine onto which my Firepick is attached and use TCP/IP to connect to it. - - -======= ->>>>>>> deleted: src/Segger_SystemView/Global.h From c17c6cc85581680962530c830b848870a46a7259 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 22:21:33 +1300 Subject: [PATCH 55/66] modified: .gitignore --- .gitignore | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d9256042..27e5908b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,16 @@ #eclipse project files .cproject .project - +.settings/ +gdbserver.log +src/Segger_SystemView/Global.h +src/Segger_SystemView/SEGGER.h +src/Segger_SystemView/SEGGER_RTT.c +src/Segger_SystemView/SEGGER_RTT.h +src/Segger_SystemView/SEGGER_RTT_Conf.h +src/Segger_SystemView/SEGGER_SYSVIEW.c +src/Segger_SystemView/SEGGER_SYSVIEW.h +src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h +src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h +src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c +src/Segger_SystemView/SEGGER_SYSVIEW_Int.h From e6c564833e39af5e6891586bba4bb72dbdffe895 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 23:34:28 +1300 Subject: [PATCH 56/66] modified: libs/ADC/BurstADC.cpp modified: libs/Adc.cpp modified: libs/Kernel.cpp modified: libs/Kernel.h new file: libs/MemoryPool.cpp new file: libs/MemoryPool.h modified: libs/Pin.cpp modified: libs/Pin.h modified: libs/RingBuffer.h modified: libs/SlowTicker.cpp modified: libs/SlowTicker.h modified: libs/StepTicker.cpp modified: libs/StepTicker.h modified: libs/StepperMotor.cpp modified: libs/StepperMotor.h modified: libs/nuts_bolts.h new file: libs/platform_memory.cpp new file: libs/platform_memory.h modified: libs/utils.cpp modified: libs/utils.h modified: modules/communication/GcodeDispatch.cpp modified: modules/robot/Block.h modified: modules/robot/Conveyor.h modified: modules/robot/Planner.cpp modified: modules/robot/Robot.cpp modified: modules/tools/endstops/Endstops.cpp modified: modules/tools/endstops/Endstops.h modified: modules/tools/laser/Laser.cpp modified: modules/tools/laser/Laser.h modified: modules/tools/spindle/Spindle.cpp modified: modules/tools/switch/Switch.cpp --- src/libs/ADC/BurstADC.cpp | 1 - src/libs/Adc.cpp | 22 +- src/libs/Kernel.cpp | 30 ++- src/libs/Kernel.h | 1 + src/libs/MemoryPool.cpp | 244 ++++++++++++++++++++ src/libs/MemoryPool.h | 58 +++++ src/libs/Pin.cpp | 2 +- src/libs/Pin.h | 8 +- src/libs/RingBuffer.h | 5 +- src/libs/SlowTicker.cpp | 30 +-- src/libs/SlowTicker.h | 4 +- src/libs/StepTicker.cpp | 74 +++--- src/libs/StepTicker.h | 12 +- src/libs/StepperMotor.cpp | 2 +- src/libs/StepperMotor.h | 7 +- src/libs/nuts_bolts.h | 4 - src/libs/platform_memory.cpp | 4 + src/libs/platform_memory.h | 12 + src/libs/utils.cpp | 17 +- src/libs/utils.h | 39 ++-- src/modules/communication/GcodeDispatch.cpp | 7 +- src/modules/robot/Block.h | 6 +- src/modules/robot/Conveyor.h | 6 +- src/modules/robot/Planner.cpp | 3 +- src/modules/robot/Robot.cpp | 3 +- src/modules/tools/endstops/Endstops.cpp | 3 - src/modules/tools/endstops/Endstops.h | 6 +- src/modules/tools/laser/Laser.cpp | 12 +- src/modules/tools/laser/Laser.h | 10 +- src/modules/tools/spindle/Spindle.cpp | 3 +- src/modules/tools/switch/Switch.cpp | 14 +- 31 files changed, 494 insertions(+), 155 deletions(-) create mode 100644 src/libs/MemoryPool.cpp create mode 100644 src/libs/MemoryPool.h create mode 100644 src/libs/platform_memory.cpp create mode 100644 src/libs/platform_memory.h mode change 100755 => 100644 src/modules/robot/Block.h mode change 100755 => 100644 src/modules/robot/Robot.cpp diff --git a/src/libs/ADC/BurstADC.cpp b/src/libs/ADC/BurstADC.cpp index c1d7f8b9..b5aa7cdd 100644 --- a/src/libs/ADC/BurstADC.cpp +++ b/src/libs/ADC/BurstADC.cpp @@ -160,7 +160,6 @@ int BurstADC::_pin_to_channel(PinName pin) { PinName BurstADC::channel_to_pin(int chan) { //LPC43xx has 2 ADC with 8 pins each + 8 adc only pins. Current structure of this function is not suitable - //TODO We need to fix this routine PinName a; return a; } diff --git a/src/libs/Adc.cpp b/src/libs/Adc.cpp index 7245f08d..82c0fd68 100644 --- a/src/libs/Adc.cpp +++ b/src/libs/Adc.cpp @@ -3,7 +3,7 @@ Smoothie 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. Smoothie 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 Smoothie. If not, see . -*/ + */ #include "Adc.h" #include "libs/nuts_bolts.h" @@ -87,8 +87,8 @@ unsigned int Adc::read(Pin *pin) __enable_irq(); #ifdef USE_MEDIAN_FILTER - // returns the median value of the last 8 samples - return median_buffer[quick_median(median_buffer, num_samples)]; + // returns the median value of the last 8 samples + return median_buffer[quick_median(median_buffer, num_samples)]; #elif defined(OVERSAMPLE) // Oversample to get 2 extra bits of resolution @@ -98,7 +98,7 @@ unsigned int Adc::read(Pin *pin) std::sort(median_buffer, median_buffer + num_samples); uint32_t sum = 0; for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) { - sum += median_buffer[i]; + sum += median_buffer[i]; } // this slows down the rate of change a little bit ave_buf[channel][3]= ave_buf[channel][2]; @@ -111,13 +111,13 @@ unsigned int Adc::read(Pin *pin) return tValue; #else - // sort the 8 readings and return the average of the middle 4 - std::sort(median_buffer, median_buffer + num_samples); - int sum = 0; - for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) { - sum += median_buffer[i]; - } - return sum / (num_samples / 2); + // sort the 8 readings and return the average of the middle 4 + std::sort(median_buffer, median_buffer + num_samples); + int sum = 0; + for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) { + sum += median_buffer[i]; + } + return sum / (num_samples / 2); #endif } diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 116a9b1f..56465829 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -12,7 +12,7 @@ #include "libs/SlowTicker.h" #include "libs/Adc.h" #include "libs/StreamOutputPool.h" -//#include +#include #include "checksumm.h" #include "ConfigValue.h" @@ -29,7 +29,7 @@ #include "Configurator.h" //#include "SimpleShell.h" -//#include "platform_memory.h" +#include "platform_memory.h" #include #include @@ -72,6 +72,8 @@ Kernel::Kernel(){ // Pre-load the config cache, do after setting up serial so we can report errors to serial this->config->config_cache_load(); + // For slow repeteative tasks + this->add_module( this->slow_ticker = new SlowTicker()); // now config is loaded we can do normal setup for serial based on config //TODO TOADDBACK when USB is implemented // delete this->serial; @@ -118,16 +120,25 @@ Kernel::Kernel(){ // we exepct ok per line now not per G code, setting this to false will return to the old (incorrect) way of ok per G code this->ok_per_line= this->config->value( ok_per_line_checksum )->by_default(true)->as_bool(); + // Configure the step ticker + this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); + float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); + // REMOVE this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(1000)->as_number(); + this->add_module( this->serial ); // HAL stuff add_module( this->slow_ticker = new SlowTicker()); this->step_ticker = new StepTicker(); -//TODO ADDBACK this->adc = new Adc(); + this->adc = new Adc(); // TODO : These should go into platform-specific files - // TODO THIS NEEDS TO BE UPDATED FOR LPC4330 + NVIC_SetPriorityGrouping(0); + NVIC_SetPriority(TIMER0_IRQn, 2); + NVIC_SetPriority(TIMER1_IRQn, 1); + NVIC_SetPriority(TIMER2_IRQn, 4); + NVIC_SetPriority(PendSV_IRQn, 3); // NVIC_SetPriorityGrouping(0); // NVIC_SetPriority(TIMER0_IRQn, 2); @@ -135,8 +146,9 @@ Kernel::Kernel(){ // NVIC_SetPriority(TIMER2_IRQn, 4); // NVIC_SetPriority(PendSV_IRQn, 3); -// // Set other priorities lower than the timers -// NVIC_SetPriority(ADC_IRQn, 5); + // Set other priorities lower than the timers + NVIC_SetPriority(ADC0_IRQn, 5); + NVIC_SetPriority(ADC1_IRQn, 5); // NVIC_SetPriority(USB_IRQn, 5); // // // If MRI is enabled @@ -152,10 +164,6 @@ Kernel::Kernel(){ // NVIC_SetPriority(UART3_IRQn, 5); // } - // Configure the step ticker - this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); - float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(1)->as_number(); - // Configure the step ticker this->step_ticker->set_frequency( this->base_stepping_frequency ); this->step_ticker->set_unstep_time( microseconds_per_step_pulse ); @@ -249,7 +257,7 @@ void Kernel::call_event(_EVENT_ENUM id_event, void * argument){ bool was_idle= true; if(id_event == ON_HALT) { this->halted= (argument == nullptr); - was_idle= conveyor->is_queue_empty(); // see if we were doing anything like printing + was_idle= conveyor->is_idle(); // see if we were doing anything like printing } // send to all registered modules diff --git a/src/libs/Kernel.h b/src/libs/Kernel.h index 84934301..8b9ccdcf 100644 --- a/src/libs/Kernel.h +++ b/src/libs/Kernel.h @@ -85,6 +85,7 @@ class Kernel { bool feed_hold:1; bool ok_per_line:1; }; + }; #endif diff --git a/src/libs/MemoryPool.cpp b/src/libs/MemoryPool.cpp new file mode 100644 index 00000000..60474ec4 --- /dev/null +++ b/src/libs/MemoryPool.cpp @@ -0,0 +1,244 @@ +#include "MemoryPool.h" + +#include "StreamOutput.h" + +#include +#include + +// this catches all usages of delete blah. The object's destructor is called before we get here +// it first checks if the deleted object is part of a pool, and uses free otherwise. +void operator delete(void* p) +{ + MemoryPool* m = MemoryPool::first; + while (m) + { + if (m->has(p)) + { + MDEBUG("Pool %p has %p, using dealloc()\n", m, p); + m->dealloc(p); + return; + } + m = m->next; + } + + MDEBUG("no pool has %p, using free()\n", p); + free(p); +} + + +#define offset(x) (((uint8_t*) x) - ((uint8_t*) this->base)) + +typedef struct __attribute__ ((packed)) +{ + uint32_t next :31; + uint32_t used :1; + + uint8_t data[]; +} _poolregion; + +MemoryPool* MemoryPool::first = NULL; + +MemoryPool::MemoryPool(void* base, uint16_t size) +{ + this->base = base; + this->size = size; + + ((_poolregion*) base)->used = 0; + ((_poolregion*) base)->next = size; + + // insert ourselves into head of LL + next = first; + first = this; +} + +MemoryPool::~MemoryPool() +{ + MDEBUG("Pool %p destroyed: region %p (%d)\n", this, base, size); + + // remove ourselves from the LL + if (first == this) + { // special case: we're first + first = this->next; + return; + } + + // otherwise search the LL for the previous pool + MemoryPool* m = first; + while (m) + { + if (m->next == this) + { + m->next = next; + return; + } + m = m->next; + } +} + +void* MemoryPool::alloc(size_t nbytes) +{ + // nbytes = ceil(nbytes / 4) * 4 + if (nbytes & 3) + nbytes += 4 - (nbytes & 3); + + // start at the start + _poolregion* p = ((_poolregion*) base); + + // find the allocation size including our metadata + uint16_t nsize = nbytes + sizeof(_poolregion); + + MDEBUG("\tallocate %d bytes from %p\n", nsize, base); + + // now we walk the list, looking for a sufficiently large free block + do { + MDEBUG("\t\tchecking %p (%s, %db)\n", p, (p->used?"used":"free"), p->next); + if ((p->used == 0) && (p->next >= nsize)) + { // we found a free space that's big enough + MDEBUG("\t\tFOUND free block at %p (%+d) with %d bytes\n", p, offset(p), p->next); + // mark it as used + p->used = 1; + + // if there's free space at the end of this block + if (p->next > nsize) + { + // q = p->next + _poolregion* q = (_poolregion*) (((uint8_t*) p) + nsize); + + MDEBUG("\t\twriting header to %p (%+d) (%d)\n", q, offset(q), p->next - nsize); + // write a new block header into q + q->used = 0; + q->next = p->next - nsize; + + // set our next to point to it + p->next = nsize; + + // sanity check + if (offset(q) >= size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + } + + // then return the data region for the block + return &p->data; + } + + // p = p->next + p = (_poolregion*) (((uint8_t*) p) + p->next); + + // make sure we don't walk off the end + } while (p <= (_poolregion*) (((uint8_t*)base) + size)); + + // fell off the end of the region! + return NULL; +} + +void MemoryPool::dealloc(void* d) +{ + _poolregion* p = (_poolregion*) (((uint8_t*) d) - sizeof(_poolregion)); + p->used = 0; + + MDEBUG("\tdeallocating %p (%+d, %db)\n", p, offset(p), p->next); + + // combine next block if it's free + _poolregion* q = (_poolregion*) (((uint8_t*) p) + p->next); + if (q->used == 0) + { + MDEBUG("\t\tCombining with next free region at %p, new size is %d\n", q, p->next + q->next); + + // sanity check + if (offset(q) > size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + + p->next += q->next; + } + + // walk the list to find previous block + q = (_poolregion*) base; + do { + // check if q is the previous block + if ((((uint8_t*) q) + q->next) == (uint8_t*) p) { + // q is the previous block. + if (q->used == 0) + { // if q is free + MDEBUG("\t\tCombining with previous free region at %p, new size is %d\n", q, p->next + q->next); + + // combine! + q->next += p->next; + + // sanity check + if ((offset(p) + p->next) >= size) + { + // captain, we have a problem! + // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full + __debugbreak(); + } + } + + // we found previous block, return + return; + } + + // return if last block + if (offset(q) + q->next >= size) + return; + + // q = q->next + q = (_poolregion*) (((uint8_t*) q) + q->next); + + // if some idiot deallocates our memory region while we're using it, strange things can happen. + // avoid an infinite loop in that case, however we'll still leak memory and may corrupt things + if (q->next == 0) + return; + + // make sure we don't walk off the end + } while (q < (_poolregion*) (((uint8_t*) base) + size)); +} + +void MemoryPool::debug(StreamOutput* str) +{ + _poolregion* p = (_poolregion*) base; + uint32_t tot = 0; + uint32_t free = 0; + str->printf("Start: %ub MemoryPool at %p\n", size, p); + do { + str->printf("\tChunk at %p (%+4d): %s, %lu bytes\n", p, offset(p), (p->used?"used":"free"), p->next); + tot += p->next; + if (p->used == 0) + free += p->next; + if ((offset(p) + p->next >= size) || (p->next <= sizeof(_poolregion))) + { + str->printf("End: total %lub, free: %lub\n", tot, free); + return; + } + p = (_poolregion*) (((uint8_t*) p) + p->next); + } while (1); +} + +bool MemoryPool::has(void* p) +{ + return ((p >= base) && (p < (void*) (((uint8_t*) base) + size))); +} + +uint32_t MemoryPool::free() +{ + uint32_t free = 0; + + _poolregion* p = (_poolregion*) base; + + do { + if (p->used == 0) + free += p->next; + if (offset(p) + p->next >= size) + return free; + if (p->next <= sizeof(_poolregion)) + return free; + p = (_poolregion*) (((uint8_t*) p) + p->next); + } while (1); +} diff --git a/src/libs/MemoryPool.h b/src/libs/MemoryPool.h new file mode 100644 index 00000000..a2a97357 --- /dev/null +++ b/src/libs/MemoryPool.h @@ -0,0 +1,58 @@ +#ifndef _MEMORYPOOL_H +#define _MEMORYPOOL_H + +#include +// #include +#include + +#ifdef MEMDEBUG + #define MDEBUG(...) printf(__VA_ARGS__) +#else + #define MDEBUG(...) do {} while (0) +#endif + +class StreamOutput; + +/* + * with MUCH thanks to http://www.parashift.com/c++-faq-lite/memory-pools.html + * + * test framework at https://gist.github.com/triffid/5563987 + */ + +class MemoryPool +{ +public: + MemoryPool(void* base, uint16_t size); + ~MemoryPool(); + + void* alloc(size_t); + void dealloc(void* p); + + void debug(StreamOutput*); + + bool has(void*); + + uint32_t free(void); + + MemoryPool* next; + + static MemoryPool* first; + +private: + void* base; + uint16_t size; +}; + +// this overloads "placement new" +inline void* operator new(size_t nbytes, MemoryPool& pool) +{ + return pool.alloc(nbytes); +} + +// this allows placement new to free memory if the constructor fails +inline void operator delete(void* p, MemoryPool& pool) +{ + pool.dealloc(p); +} + +#endif /* _MEMORYPOOL_H */ diff --git a/src/libs/Pin.cpp b/src/libs/Pin.cpp index c3fe78bb..bc2ec29d 100644 --- a/src/libs/Pin.cpp +++ b/src/libs/Pin.cpp @@ -47,7 +47,7 @@ Pin* Pin::from_string(std::string value){ // grab first integer as port. pointer to first non-digit goes in cn - port_number = strtol(cs, &cn, 16); //allow hexadecimal port numbers + port_number = strtol(cs, &cn, 16); //allow hexadecimal portnumbes // if cn > cs then strtol read at least one digit if ((cn > cs) && (port_number <= 15)){ diff --git a/src/libs/Pin.h b/src/libs/Pin.h index 8938ddd7..d4a01019 100644 --- a/src/libs/Pin.h +++ b/src/libs/Pin.h @@ -5,7 +5,6 @@ #include #include -//#include "libs/LPC17xx/sLPC17xx.h" // smoothed mbed.h lib #include "PinNames.h" namespace mbed { @@ -26,7 +25,7 @@ class Pin { } inline bool equals(const Pin& other) const { - //return (this->pin == other.pin) && (this->port == other.port); //TODO this needs to be fixed + return (this->pin_number == other.pin_number) && (this->port_number == other.port_number); return false; } @@ -88,11 +87,14 @@ class Pin { PinName getPinName() {return pinName; } int port_number; - int pin_number; + int pin_number; private: //Added pinName PinName pinName; }; + + + #endif diff --git a/src/libs/RingBuffer.h b/src/libs/RingBuffer.h index 3ae9ec6b..24b25621 100644 --- a/src/libs/RingBuffer.h +++ b/src/libs/RingBuffer.h @@ -3,7 +3,6 @@ Smoothie 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. Smoothie 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 Smoothie. If not, see . - With chucks taken from http://en.wikipedia.org/wiki/Circular_buffer, see licence there also */ @@ -40,9 +39,9 @@ template int RingBuffer::capacity(){ } template int RingBuffer::size(){ - // TOADDBACK __disable_irq(); + __asm volatile ("cpsid i"); // equivalent to CMSIS '__disable_irq()' function int i = head - tail + ((tail > head)?length:0); - // TOADDBACK __enable_irq(); + __asm volatile ("cpsie i"); // equivalent to CMSIS '__enable_irq()' function return i; } diff --git a/src/libs/SlowTicker.cpp b/src/libs/SlowTicker.cpp index 0e1c2c75..b12b2d30 100644 --- a/src/libs/SlowTicker.cpp +++ b/src/libs/SlowTicker.cpp @@ -15,7 +15,6 @@ using namespace std; #include "libs/Hook.h" #include "modules/robot/Conveyor.h" #include "Gcode.h" -#include "mbed.h" #include @@ -29,17 +28,23 @@ SlowTicker* global_slow_ticker; SlowTicker::SlowTicker(){ global_slow_ticker = this; - this->ticker = new Ticker(); // ISP button FIXME: WHy is this here? - ispbtn.from_string("2.10")->as_input()->pull_up(); + // TOADDBACK ispbtn.from_string("2.10")->as_input()->pull_up(); + + uint32_t PCLK = SystemCoreClock; + uint32_t prescale = PCLK / 1000000; //Increment MR each uSecond - //LPC_SC->PCONP |= (1 << 22); // Power Ticker ON - /* Enable timer 0 clock and reset it */ + /* Enable timer 1 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER2].CFG |= 1; - LPC_TIMER2->MCR = 3; // Match on MR0, reset on MR0 - // do not enable interrupt until setup is complete - LPC_TIMER2->TCR = 0; // Disable interrupt + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER2_RST & 31); //Trigger a peripheral reset for the timer + while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER2_RST & 31)))){} + /* Configure Timer 0 */ + LPC_TIMER2->CTCR = 0x0; // timer mode + LPC_TIMER2->TCR = 0; // Disable interrupt + LPC_TIMER2->PR = prescale - 1; + LPC_TIMER2->MR[0] = 10000000; // Initial dummy value for Match Register + LPC_TIMER2->MCR |= 3; // match on Mr0, stop on match max_frequency = 5; // initial max frequency is set to 5Hz set_frequency(max_frequency); @@ -58,13 +63,11 @@ void SlowTicker::on_module_loaded(){ // Set the base frequency we use for all sub-frequencies void SlowTicker::set_frequency( int frequency ){ - this->interval = (float)1 / frequency; - //this->interval = (float)1000000 / frequency; //this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second + this->interval = SystemCoreClock / frequency; // SystemCoreClock = Timer increments in a second LPC_TIMER2->MR[0] = this->interval; LPC_TIMER2->TCR = 3; // Reset LPC_TIMER2->TCR = 1; // Reset flag_1s_count= SystemCoreClock>>2; -// this->ticker->attach_us(this, &SlowTicker::tick,(int)( this->interval * (float)1000000) ); } // The actual interrupt being called by the timer, this is where work is done @@ -93,8 +96,10 @@ void SlowTicker::tick(){ // Enter MRI mode if the ISP button is pressed // TODO: This should have it's own module + /* TOADDBACK if (ispbtn.get() == 0) __debugbreak(); + */ } @@ -116,7 +121,7 @@ bool SlowTicker::flag_1s(){ __enable_irq(); return false; } - +#include "mbed.h" extern DigitalOut leds[]; void SlowTicker::on_idle(void*) { @@ -140,4 +145,3 @@ extern "C" void TIMER2_IRQHandler (void){ global_slow_ticker->tick(); SEGGER_SYSVIEW_RecordExitISR(); } - diff --git a/src/libs/SlowTicker.h b/src/libs/SlowTicker.h index 621fec41..08b4be6d 100644 --- a/src/libs/SlowTicker.h +++ b/src/libs/SlowTicker.h @@ -19,7 +19,6 @@ using namespace std; #include "libs/Pin.h" #include -#include "mbed.h" class SlowTicker : public Module{ public: @@ -34,7 +33,7 @@ class SlowTicker : public Module{ // TODO replace this with std::function() template Hook* attach( uint32_t frequency, T *optr, uint32_t ( T::*fptr )( uint32_t ) ){ Hook* hook = new Hook(); - hook->interval = ((float)1)/frequency; + hook->interval = floorf((SystemCoreClock)/frequency); hook->attach(optr, fptr); hook->countdown = hook->interval; @@ -55,7 +54,6 @@ class SlowTicker : public Module{ vector hooks; uint32_t max_frequency; uint32_t interval; - Ticker* ticker; Pin ispbtn; protected: diff --git a/src/libs/StepTicker.cpp b/src/libs/StepTicker.cpp index 634759be..cc9dbe63 100644 --- a/src/libs/StepTicker.cpp +++ b/src/libs/StepTicker.cpp @@ -16,7 +16,6 @@ #include "Block.h" #include "Conveyor.h" -//#include "system_LPC17xx.h" // mbed.h lib #include #include @@ -32,42 +31,46 @@ GPIO stepticker_debug_pin(STEPTICKER_DEBUG_PIN); #define SET_STEPTICKER_DEBUG_PIN(n) #endif + +// StepTicker handles the base frequency ticking for the Stepper Motors / Actuators +// It has a list of those, and calls their tick() functions at regular intervals +// They then do Bresenham stuff themselves + StepTicker *StepTicker::instance; -StepTicker::StepTicker() -{ +StepTicker::StepTicker(){ + instance = this; // setup the Singleton instance of the stepticker uint32_t PCLK = SystemCoreClock; - uint32_t prescale = PCLK / 10000000; + uint32_t prescale = PCLK / 1000000; //Increment MR each uSecond /* Enable timer 0 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER0].CFG |= 1; - LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER0_RST & 31); //Trigger a peripheral reset for the timer + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER0_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER0_RST & 31)))){} /* Configure Timer 0 */ LPC_TIMER0->CTCR = 0x0; // timer mode LPC_TIMER0->TCR = 0; // Disable interrupt LPC_TIMER0->PR = prescale - 1; LPC_TIMER0->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER0->MCR |= 3; // Interrupt on MR0, reset on MR0 - NVIC_SetPriority(TIMER0_IRQn,0); + LPC_TIMER0->MCR |= 3; // Match on MR0, reset on MR0 /* Enable timer 1 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER1].CFG |= 1; - LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer + LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER1_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER1_RST & 31)))){} - - /* Configure Timer 1 */ + /* Configure Timer 0 */ LPC_TIMER1->CTCR = 0x0; // timer mode LPC_TIMER1->TCR = 0; // Disable interrupt LPC_TIMER1->PR = prescale - 1; LPC_TIMER1->MR[0] = 10000000; // Initial dummy value for Match Register - LPC_TIMER1->MCR |= 5; // Interrupt on MR0, stop on MR0 + LPC_TIMER1->MCR |= 5; // match on Mr0, stop on match + // Default start values this->set_frequency(100000); - this->set_unstep_time(100); + this->set_unstep_time(10); this->unstep.reset(); this->num_motors = 0; @@ -75,11 +78,12 @@ StepTicker::StepTicker() this->running = false; this->current_block = nullptr; - #ifdef STEPTICKER_DEBUG_PIN +#ifdef STEPTICKER_DEBUG_PIN // setup debug pin if defined stepticker_debug_pin.output(); stepticker_debug_pin= 0; - #endif +#endif + } StepTicker::~StepTicker() @@ -98,19 +102,18 @@ void StepTicker::start() void StepTicker::set_frequency( float frequency ) { this->frequency = frequency; - this->period_us = (int)lrint(1000000/frequency); + this->period = (int)lrint(1000000/frequency); - /* Update Timer 0 Match register and reset timer */ - LPC_TIMER0->MR[0] = this->period_us; - LPC_TIMER0->TCR = 3; // Reset - LPC_TIMER0->TCR = 1; // start + LPC_TIMER0->MR[0] = this->period; + LPC_TIMER0->TCR = 3; // Reset + LPC_TIMER0->TCR = 1; // start } -// Set the reset delay -void StepTicker::set_unstep_time( float microseconds ){ - this->reset_delay_us = (int)lrint(microseconds); - /* Update Timer 1 Match register */ - LPC_TIMER1->MR[0] = this->reset_delay_us; +// Set the reset delay, must be called after set_frequency +void StepTicker::set_unstep_time( float microseconds ) +{ + uint32_t delay = (int)lrint(microseconds); + LPC_TIMER1->MR[0] = delay; // TODO check that the unstep time is less than the step period, if not slow down step ticker } @@ -126,26 +129,29 @@ void StepTicker::unstep_tick() this->unstep.reset(); } - // Unstep timer interrupt handler extern "C" void TIMER1_IRQHandler (void) { // SEGGER_RTT_LOCK(); // SEGGER_SYSVIEW_RecordEnterISR(); - LPC_TIMER1->IR |= 1 << 0; - StepTicker::getInstance()->unstep_tick(); + + LPC_TIMER1->IR |= 1 << 0; + StepTicker::getInstance()->unstep_tick(); + NVIC_ClearPendingIRQ(TIMER1_IRQn); // SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); } -// Step timer interrupt handler +// The actual interrupt handler where we do all the work extern "C" void TIMER0_IRQHandler (void) { // SEGGER_RTT_LOCK(); SEGGER_SYSVIEW_RecordEnterISR(); - LPC_TIMER0->IR |= 1 << 0; - StepTicker::getInstance()->step_tick(); + + // Reset interrupt register + LPC_TIMER0->IR |= 1 << 0; + StepTicker::getInstance()->step_tick(); NVIC_ClearPendingIRQ(TIMER0_IRQn); SEGGER_SYSVIEW_RecordExitISR(); // SEGGER_RTT_UNLOCK(); @@ -245,10 +251,9 @@ void StepTicker::step_tick (void) // Note there could be a race here if we run another tick before the unsteps have happened, // right now it takes about 3-4us but if the unstep were near 10uS or greater it would be an issue // also it takes at least 2us to get here so even when set to 1us pulse width it will still be about 3us - if( this->unstep.any()){ - /* Start Unstep timer (Timer 1) */ - LPC_TIMER1->TCR = 0x3; // reset - LPC_TIMER1->TCR = 1; // enable = 1, reset = 0 + if( unstep.any()) { + LPC_TIMER1->TCR = 3; + LPC_TIMER1->TCR = 1; } @@ -318,3 +323,4 @@ int StepTicker::register_motor(StepperMotor* m) motor[num_motors++] = m; return num_motors - 1; } + diff --git a/src/libs/StepTicker.h b/src/libs/StepTicker.h index 63e443f7..46590e08 100644 --- a/src/libs/StepTicker.h +++ b/src/libs/StepTicker.h @@ -7,10 +7,11 @@ -#pragma once +#ifndef STEPTICKER_H +#define STEPTICKER_H #include -#include +#include #include #include #include @@ -46,10 +47,6 @@ class StepTicker{ static StepTicker *getInstance() { return instance; } - // TOADDBACK was private - volatile uint32_t tick_cnt; - uint32_t period_us; - uint32_t reset_delay_us; private: static StepTicker *instance; @@ -68,3 +65,6 @@ class StepTicker{ uint8_t num_motors:4; }; }; + + +#endif diff --git a/src/libs/StepperMotor.cpp b/src/libs/StepperMotor.cpp index f2183cee..799af7dd 100644 --- a/src/libs/StepperMotor.cpp +++ b/src/libs/StepperMotor.cpp @@ -15,7 +15,7 @@ StepperMotor::StepperMotor(Pin &step, Pin &dir, Pin &en) : step_pin(step), dir_pin(dir), en_pin(en) { - //TODO ADDBACK set_high_on_debug(en.port_number, en.pin_number); + //TOADDBACK set_high_on_debug(en.port_number, en.pin); steps_per_mm = 1.0F; max_rate = 50.0F; diff --git a/src/libs/StepperMotor.h b/src/libs/StepperMotor.h index 5b017fb7..af9d245f 100644 --- a/src/libs/StepperMotor.h +++ b/src/libs/StepperMotor.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef STEPPERMOTOR_H +#define STEPPERMOTOR_H #include "Module.h" #include "Pin.h" @@ -79,3 +80,7 @@ class StepperMotor : public Module { bool selected:1; }; }; + + +#endif + diff --git a/src/libs/nuts_bolts.h b/src/libs/nuts_bolts.h index 5f799586..f17f4e99 100644 --- a/src/libs/nuts_bolts.h +++ b/src/libs/nuts_bolts.h @@ -1,19 +1,15 @@ /* nuts_bolts.h - cartesian robot controller. 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 . */ diff --git a/src/libs/platform_memory.cpp b/src/libs/platform_memory.cpp new file mode 100644 index 00000000..f80b287c --- /dev/null +++ b/src/libs/platform_memory.cpp @@ -0,0 +1,4 @@ +#include "platform_memory.h" + +MemoryPool* _AHB0; +MemoryPool* _AHB1; diff --git a/src/libs/platform_memory.h b/src/libs/platform_memory.h new file mode 100644 index 00000000..71ffe2c0 --- /dev/null +++ b/src/libs/platform_memory.h @@ -0,0 +1,12 @@ +#ifndef _PLATFORM_MEMORY_H +#define _PLATFORM_MEMORY_H + +#include "MemoryPool.h" + +#define AHB0 (*_AHB0) +#define AHB1 (*_AHB1) + +extern MemoryPool* _AHB0; +extern MemoryPool* _AHB1; + +#endif /* _PLATFORM_MEMORY_H */ diff --git a/src/libs/utils.cpp b/src/libs/utils.cpp index 9bb9c8ac..273a5991 100644 --- a/src/libs/utils.cpp +++ b/src/libs/utils.cpp @@ -263,18 +263,13 @@ string wcs2gcode(int wcs) { return str; } -void safe_delay_ms(uint32_t delay) +void safe_delay(uint32_t delayms) { - safe_delay_us(delay*1000); -} - -void safe_delay_us(uint32_t dus) -{ - uint32_t start = us_ticker_read(); - while ((us_ticker_read() - start) < dus) { + uint32_t dus= delayms*1000; //convert ms to us + uint32_t counter = 0; + while( counter < dus ){ THEKERNEL->call_event(ON_IDLE); -//TODO determine if below code is still necessary -// counter++; -// wait_us(1); + counter++; + wait_us(1); } } diff --git a/src/libs/utils.h b/src/libs/utils.h index d8aa7eb8..4cb3025e 100644 --- a/src/libs/utils.h +++ b/src/libs/utils.h @@ -1,10 +1,14 @@ -#pragma once +#ifndef UTILS_H +#define UTILS_H #include #include #include -std::string lc(const std::string& str); +using std::string; +using std::vector; + +string lc(const string& str); bool is_alpha( int ); bool is_digit( int ); @@ -12,32 +16,27 @@ bool is_numeric( int ); bool is_alphanum( int ); bool is_whitespace( int ); -std::vector split(const char *str, char c = ','); -std::vector parse_number_list(const char *str); -std::vector parse_number_list(const char *str, uint8_t radix); +vector split(const char *str, char c = ','); +vector parse_number_list(const char *str); +vector parse_number_list(const char *str, uint8_t radix); -std::string remove_non_number( std::string str ); +string remove_non_number( string str ); -uint16_t get_checksum(const std::string& to_check); +uint16_t get_checksum(const string& to_check); uint16_t get_checksum(const char* to_check); -void get_checksums(uint16_t check_sums[], const std::string& key); - -std::string shift_parameter( std::string ¶meters ); +void get_checksums(uint16_t check_sums[], const string& key); -std::string get_arguments( const std::string& possible_command ); +string shift_parameter( string ¶meters ); -bool file_exists( const std::string file_name ); +string get_arguments( const string& possible_command ); -void system_reset( bool dfu= false ); +bool file_exists( const string file_name ); -std::string absolute_from_relative( std::string path ); +string absolute_from_relative( string path ); int append_parameters(char *buf, std::vector> params, size_t bufsize); -std::string wcs2gcode(int wcs); -void safe_delay_us(uint32_t delay); -void safe_delay_ms(uint32_t delay); - -#define confine(value, min, max) (((value) < (min))?(min):(((value) > (max))?(max):(value))) +string wcs2gcode(int wcs); +void safe_delay(uint32_t delay); -//#define dd(...) LPC_GPIO2->FIODIR = 0xffff; LPC_GPIO2->FIOCLR = 0xffff; LPC_GPIO2->FIOSET = __VA_ARGS__ +#endif diff --git a/src/modules/communication/GcodeDispatch.cpp b/src/modules/communication/GcodeDispatch.cpp index 9ae3bcca..6ec38514 100644 --- a/src/modules/communication/GcodeDispatch.cpp +++ b/src/modules/communication/GcodeDispatch.cpp @@ -269,7 +269,7 @@ void GcodeDispatch::on_console_line_received(void *line) while(is_whitespace(str.front())){ str= str.substr(1); } // strip leading whitespace delete gcode; - // TODOĀ :Ā TOADDBACK When Simpleshell is added back in + // TOADDBACK When Simpleshell is added back in /* if(str.empty()) { SimpleShell::parse_command("help", "", new_message.stream); @@ -290,7 +290,6 @@ void GcodeDispatch::on_console_line_received(void *line) case 500: // M500 save volatile settings to config-override THEKERNEL->conveyor->wait_for_idle(); //just to be safe as it can take a while to run - //remove(THEKERNEL->config_override_filename()); // seems to cause a hang every now and then // TOADDBACK :Ā __disable_irq(); { FileStream fs(THEKERNEL->config_override_filename()); @@ -313,8 +312,8 @@ void GcodeDispatch::on_console_line_received(void *line) string arg= get_arguments(single_command + possible_command); // rest of line is filename if(arg.empty()) arg= "/sd/config-override"; else arg= "/sd/config-override." + arg; - //new_message.stream->printf("args: <%s>\n", arg.c_str()); - //TODO ADDBACK SimpleShell::parse_command((gcode->m == 501) ? "load_command" : "save_command", arg, new_message.stream); + new_message.stream->printf("args: <%s>\n", arg.c_str()); + //TOADDBACKĀ : SimpleShell::parse_command((gcode->m == 501) ? "load_command" : "save_command", arg, new_message.stream); } delete gcode; new_message.stream->printf("ok\r\n"); diff --git a/src/modules/robot/Block.h b/src/modules/robot/Block.h old mode 100755 new mode 100644 index 3df796ce..da5c1996 --- a/src/modules/robot/Block.h +++ b/src/modules/robot/Block.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef BLOCK_H +#define BLOCK_H #include #include @@ -77,3 +78,6 @@ class Block { uint16_t s_value:12; // for laser 1.11 Fixed point }; }; + + +#endif diff --git a/src/modules/robot/Conveyor.h b/src/modules/robot/Conveyor.h index ba2b09a7..9ea8e40a 100644 --- a/src/modules/robot/Conveyor.h +++ b/src/modules/robot/Conveyor.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef CONVEYOR_H +#define CONVEYOR_H #include "libs/Module.h" #include "HeapRing.h" @@ -63,3 +64,6 @@ class Conveyor : public Module }; }; + + +#endif // CONVEYOR_H diff --git a/src/modules/robot/Planner.cpp b/src/modules/robot/Planner.cpp index 1432e44a..09ef0a83 100644 --- a/src/modules/robot/Planner.cpp +++ b/src/modules/robot/Planner.cpp @@ -53,7 +53,8 @@ void Planner::config_load() bool Planner::append_block( ActuatorCoordinates &actuator_pos, uint8_t n_motors, float rate_mm_s, float distance, float *unit_vec, float acceleration, float s_value, bool g123) { // Create ( recycle ) a new block - Block* block = THECONVEYOR->queue.head_ref(); + //Block* block = THECONVEYOR->queue.head_ref(); + Block* block = THEKERNEL->conveyor->queue.head_ref(); // Direction bits bool has_steps = false; diff --git a/src/modules/robot/Robot.cpp b/src/modules/robot/Robot.cpp old mode 100755 new mode 100644 index d9cf882b..e9512ba1 --- a/src/modules/robot/Robot.cpp +++ b/src/modules/robot/Robot.cpp @@ -178,7 +178,7 @@ void Robot::load_config() // in mm/sec but specified in config as mm/min this->max_speeds[X_AXIS] = THEKERNEL->config->value(x_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; this->max_speeds[Y_AXIS] = THEKERNEL->config->value(y_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; - this->max_speeds[Z_AXIS] = THEKERNEL->config->value(z_axis_max_speed_checksum )->by_default(60000.0F)->as_number() / 60.0F; + this->max_speeds[Z_AXIS] = THEKERNEL->config->value(z_axis_max_speed_checksum )->by_default( 300.0F)->as_number() / 60.0F; this->segment_z_moves = THEKERNEL->config->value(segment_z_moves_checksum )->by_default(true)->as_bool(); this->save_g92 = THEKERNEL->config->value(save_g92_checksum )->by_default(false)->as_bool(); @@ -1001,6 +1001,7 @@ bool Robot::append_milestone(const float target[], float rate_mm_s) // as the last milestone won't be updated we do not actually lose any moves as they will be accounted for in the next move if(!auxilliary_move && distance < 0.00001F) return false; + if(!auxilliary_move) { for (size_t i = X_AXIS; i <= Z_AXIS; i++) { // find distance unit vector for primary axis only diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index c36611bc..0b462fd4 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -844,10 +844,8 @@ void Endstops::on_gcode_received(void *argument) if(gcode->subcode == 0 || gcode->subcode == 2) { // Enable the motors THEKERNEL->stepper->turn_enable_pins_on(); - int32_t x = 0, y = 0, z = 0, f = 200 * 16; if (gcode->has_letter('F')) f = gcode->get_value('F'); - if (gcode->has_letter('X')) { float v = gcode->get_value('X'); if(gcode->subcode == 2) x = lroundf(v * STEPS_PER_MM(X_AXIS)); @@ -867,7 +865,6 @@ void Endstops::on_gcode_received(void *argument) STEPPER[Z_AXIS]->move(z < 0, abs(z), f); } gcode->stream->printf("Moving X %ld Y %ld Z %ld steps at F %ld steps/sec\n", x, y, z, f); - } else if(gcode->subcode == 1) { // stop any that are moving for (int i = 0; i < 3; ++i) { diff --git a/src/modules/tools/endstops/Endstops.h b/src/modules/tools/endstops/Endstops.h index 387781be..2089b983 100644 --- a/src/modules/tools/endstops/Endstops.h +++ b/src/modules/tools/endstops/Endstops.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef ENDSTOPS_MODULE_H +#define ENDSTOPS_MODULE_H #include "libs/Module.h" #include "libs/Pin.h" @@ -66,3 +67,6 @@ class Endstops : public Module{ bool move_to_origin_after_home:1; }; }; + + +#endif diff --git a/src/modules/tools/laser/Laser.cpp b/src/modules/tools/laser/Laser.cpp index 796338fb..04decc74 100644 --- a/src/modules/tools/laser/Laser.cpp +++ b/src/modules/tools/laser/Laser.cpp @@ -46,8 +46,9 @@ Laser::Laser() manual_fire= false; } -void Laser::on_module_loaded() { - if( !THEKERNEL->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ){ +void Laser::on_module_loaded() +{ + if( !THEKERNEL->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ) { // as not needed free up resource delete this; return; @@ -63,8 +64,7 @@ void Laser::on_module_loaded() { pwm_pin = dummy_pin->hardware_pwm(); - if (pwm_pin == nullptr) - { + if (pwm_pin == NULL) { // TOADDBACK THEKERNEL->streams->printf("Error: Laser cannot use P%d.%d (P2.0 - P2.5, P1.18, P1.20, P1.21, P1.23, P1.24, P1.26, P3.25, P3.26 only). Laser module disabled.\n", dummy_pin->port_number, dummy_pin->pin); delete dummy_pin; delete this; @@ -107,10 +107,6 @@ void Laser::on_module_loaded() { set_laser_power(0); //register for events - this->register_for_event(ON_GCODE_EXECUTE); - this->register_for_event(ON_SPEED_CHANGE); - this->register_for_event(ON_BLOCK_BEGIN); - this->register_for_event(ON_BLOCK_END); this->register_for_event(ON_HALT); this->register_for_event(ON_GCODE_RECEIVED); this->register_for_event(ON_CONSOLE_LINE_RECEIVED); diff --git a/src/modules/tools/laser/Laser.h b/src/modules/tools/laser/Laser.h index 7978e291..49f6df2e 100644 --- a/src/modules/tools/laser/Laser.h +++ b/src/modules/tools/laser/Laser.h @@ -5,7 +5,8 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#pragma once +#ifndef LASER_MODULE_H +#define LASER_MODULE_H #include "libs/Module.h" @@ -38,7 +39,7 @@ class Laser : public Module{ float current_speed_ratio(const Block *block) const; mbed::PwmOut *pwm_pin; // PWM output to regulate the laser power - Pin *ttl_pin; // TTL output to fire laser + Pin *ttl_pin; // TTL output to fire laser float laser_maximum_power; // maximum allowed laser power to be output on the pwm pin float laser_minimum_power; // value used to tickle the laser on moves. Also minimum value for auto-scaling float laser_maximum_s_value; // Value of S code that will represent max power @@ -46,8 +47,11 @@ class Laser : public Module{ struct { bool laser_on:1; // set if the laser is on bool pwm_inverting:1; // stores whether the PWM period should be inverted - bool ttl_used:1; // stores whether we have a TTL output + bool ttl_used:1; // stores whether we have a TTL output bool ttl_inverting:1; // stores whether the TTL output should be inverted bool manual_fire:1; // set when manually firing }; }; + + +#endif diff --git a/src/modules/tools/spindle/Spindle.cpp b/src/modules/tools/spindle/Spindle.cpp index 879b8eec..e483b84a 100644 --- a/src/modules/tools/spindle/Spindle.cpp +++ b/src/modules/tools/spindle/Spindle.cpp @@ -15,8 +15,7 @@ #include "StreamOutputPool.h" #include "SlowTicker.h" #include "Conveyor.h" -//#include "system_LPC17xx.h" -#include "utils.h" +#include "nuts_bolts.h" #include "libs/Pin.h" #include "mbed.h" diff --git a/src/modules/tools/switch/Switch.cpp b/src/modules/tools/switch/Switch.cpp index 0b27f8a6..1957562e 100644 --- a/src/modules/tools/switch/Switch.cpp +++ b/src/modules/tools/switch/Switch.cpp @@ -24,7 +24,7 @@ #include "PwmOut.h" -//TODO ADDBACK #include "MRI_Hooks.h" +// TOADDBACK #include "MRI_Hooks.h" #include @@ -106,9 +106,9 @@ void Switch::on_config_reload(void *argument) this->sigmadelta_pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); if(this->sigmadelta_pin->connected()) { if(failsafe == 1) { - //TODO ADDBACK set_high_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); + // TOADDBACKĀ set_high_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); }else{ - //TODO ADDBACK set_low_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); + // TOADDBACK set_low_on_debug(sigmadelta_pin->port_number, sigmadelta_pin->pin); } }else{ this->output_type= NONE; @@ -122,9 +122,9 @@ void Switch::on_config_reload(void *argument) this->digital_pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); if(this->digital_pin->connected()) { if(failsafe == 1) { - //TODO ADDBACK set_high_on_debug(digital_pin->port_number, digital_pin->pin); + // TOADDBACKĀ set_high_on_debug(digital_pin->port_number, digital_pin->pin); }else{ - //TODO ADDBACK set_low_on_debug(digital_pin->port_number, digital_pin->pin); + // TOADDBACK set_low_on_debug(digital_pin->port_number, digital_pin->pin); } }else{ this->output_type= NONE; @@ -138,9 +138,9 @@ void Switch::on_config_reload(void *argument) pin->from_string(THEKERNEL->config->value(switch_checksum, this->name_checksum, output_pin_checksum )->by_default("nc")->as_string())->as_output(); this->pwm_pin= pin->hardware_pwm(); if(failsafe == 1) { - //TODO ADDBACK set_high_on_debug(pin->port_number, pin->pin); + // TOADDBACKĀ set_high_on_debug(pin->port_number, pin->pin); }else{ - //TODO ADDBACK set_low_on_debug(pin->port_number, pin->pin); + // TOADDBACK set_low_on_debug(pin->port_number, pin->pin); } delete pin; if(this->pwm_pin == nullptr) { From bfeca01121ddb1f157c7bade9b6bafad17770ae5 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 23:54:22 +1300 Subject: [PATCH 57/66] modified: src/main.cpp --- src/main.cpp | 55 ++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 263b1830..e8c9167e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,8 +56,6 @@ #include "SEGGER_SYSVIEW.h" #include "version.h" -#include "system_LPC43xx.h" -//#include "platform_memory.h" #include "mbed.h" @@ -110,28 +108,28 @@ void init() { #ifdef CNC kernel->streams->printf(" CNC Build\r\n"); #endif -#ifdef DISABLEMSD - kernel->streams->printf(" NOMSD Build\r\n"); -#endif bool sdok= false; //TODO remove once SD Card code is working // bool sdok= (sd.disk_initialize() == 0); // if(!sdok) kernel->streams->printf("SDCard failed to initialize\r\n"); +// #ifdef NONETWORK +// kernel->streams->printf("NETWORK is disabled\r\n"); +// #endif -#ifdef DISABLEMSD - // attempt to be able to disable msd in config - if(sdok && !kernel->config->value( disable_msd_checksum )->by_default(false)->as_bool()){ - // HACK to zero the memory USBMSD uses as it and its objects seem to not initialize properly in the ctor - size_t n= sizeof(USBMSD); - void *v = AHB0.alloc(n); - memset(v, 0, n); // clear the allocated memory - msc= new(v) USBMSD(&u, &sd); // allocate object using zeroed memory - }else{ - msc= NULL; - kernel->streams->printf("MSD is disabled\r\n"); - } -#endif +//#ifdef DISABLEMSD +// // attempt to be able to disable msd in config +// if(sdok && !kernel->config->value( disable_msd_checksum )->by_default(false)->as_bool()){ +// // HACK to zero the memory USBMSD uses as it and its objects seem to not initialize properly in the ctor +// size_t n= sizeof(USBMSD); +// void *v = AHB0.alloc(n); +// memset(v, 0, n); // clear the allocated memory +// msc= new(v) USBMSD(&u, &sd); // allocate object using zeroed memory +// }else{ +// msc= NULL; +// kernel->streams->printf("MSD is disabled\r\n"); +// } +//#endif // Create and add main modules // kernel->add_module( new Player() ); @@ -146,7 +144,6 @@ bool sdok= false; //TODO remove once SD Card code is working #endif #ifndef NO_TOOLS_SWITCH - // Create all Switch modules SwitchPool *sp= new SwitchPool(); sp->load_tools(); delete sp; @@ -233,12 +230,12 @@ bool sdok= false; //TODO remove once SD Card code is working // memory before cache is cleared //SimpleShell::print_mem(kernel->streams); - // Clear the configuration cache as it is no longer needed + // clear up the config cache to save some memory kernel->config->config_cache_clear(); if(kernel->is_using_leds()) { // set some leds to indicate status... led0 init done, led1 mainloop running, led2 idle loop running, led3 sdcard ok - leds[0]= 0; // indicate we are done with init + leds[0]= 1; // indicate we are done with init leds[3]= sdok?1:0; // 4th led indicates sdcard is available (TODO maye should indicate config was found) } @@ -261,26 +258,24 @@ bool sdok= false; //TODO remove once SD Card code is working } // start the timers and interrupts - kernel->conveyor->start(THEROBOT->get_number_registered_motors()); - kernel->step_ticker->start(); + THEKERNEL->conveyor->start(THEROBOT->get_number_registered_motors()); + THEKERNEL->step_ticker->start(); THEKERNEL->slow_ticker->start(); } -int main() { - +int main() +{ init(); - - uint16_t cnt = 0; - + int cnt = 0; // Main loop while(1){ if(THEKERNEL->is_using_leds()) { // flash led 2 to show we are alive - leds[1]= (cnt++ & 0x1000) ? 1 : 0; + leds[0]= (cnt++ & 0x1000) ? 1 : 0; } THEKERNEL->call_event(ON_MAIN_LOOP); THEKERNEL->call_event(ON_IDLE); + THEKERNEL->streams->printf("0"); } } - From b5ed7cabdf821d7fb0abde9beb3fbdf0e4eae0ba Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 10 Nov 2016 23:56:38 +1300 Subject: [PATCH 58/66] modified: src/main.cpp --- src/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index e8c9167e..bd8b0cbf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -275,7 +275,6 @@ int main() } THEKERNEL->call_event(ON_MAIN_LOOP); THEKERNEL->call_event(ON_IDLE); - THEKERNEL->streams->printf("0"); } } From f6163a311399d57cd6847c9a5433f8e68b758916 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Fri, 11 Nov 2016 00:33:40 +1300 Subject: [PATCH 59/66] modified: src/libs/SlowTicker.cpp --- src/libs/SlowTicker.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/SlowTicker.cpp b/src/libs/SlowTicker.cpp index 65c6e68d..577d541c 100644 --- a/src/libs/SlowTicker.cpp +++ b/src/libs/SlowTicker.cpp @@ -18,6 +18,9 @@ using namespace std; #include +#include "SEGGER_SYSVIEW.h" +#include "SEGGER_RTT_Conf.h" + // This module uses a Timer to periodically call hooks // Modules register with a function ( callback ) and a frequency, and we then call that function at the given frequency. @@ -31,11 +34,12 @@ SlowTicker::SlowTicker(){ uint32_t PCLK = SystemCoreClock; uint32_t prescale = PCLK / 1000000; //Increment MR each uSecond + /* Enable timer 1 clock and reset it */ LPC_CCU1->CLKCCU[CLK_MX_TIMER2].CFG |= 1; LPC_RGU->RESET_CTRL1 = 1 << (RGU_TIMER2_RST & 31); //Trigger a peripheral reset for the timer while (!(LPC_RGU->RESET_ACTIVE_STATUS1 & (1 << (RGU_TIMER2_RST & 31)))){} - /* Configure Timer 0 */ + /* Configure Timer 2 */ LPC_TIMER2->CTCR = 0x0; // timer mode LPC_TIMER2->TCR = 0; // Disable interrupt LPC_TIMER2->PR = prescale - 1; @@ -134,8 +138,14 @@ void SlowTicker::on_idle(void*) } extern "C" void TIMER2_IRQHandler (void){ + // SEGGER_RTT_LOCK(); + SEGGER_SYSVIEW_RecordEnterISR(); + if((LPC_TIMER2->IR >> 0) & 1){ // If interrupt register set for MR0 LPC_TIMER2->IR |= 1 << 0; // Reset it } global_slow_ticker->tick(); + // NVIC_ClearPendingIRQ(TIMER2_IRQn); + SEGGER_SYSVIEW_RecordExitISR(); + // SEGGER_RTT_UNLOCK(); } From bf7d79fd6242ec10af76439aaa29c44a23325b13 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Fri, 11 Nov 2016 23:07:23 +1300 Subject: [PATCH 60/66] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 27e5908b..a0b3a9cd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ src/Segger_SystemView/SEGGER_SYSVIEW_Conf.h src/Segger_SystemView/SEGGER_SYSVIEW_ConfDefaults.h src/Segger_SystemView/SEGGER_SYSVIEW_Config_NoOS.c src/Segger_SystemView/SEGGER_SYSVIEW_Int.h +README.md From e7dc42934ce13d615c8731523af6618870ae626d Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 12 Nov 2016 11:58:09 +1300 Subject: [PATCH 61/66] modified: makefile modified: src/libs/Kernel.cpp modified: src/main.cpp modified: src/makefile modified: src/modules/tools/endstops/Endstops.cpp --- makefile | 154 ++++++------------------ src/libs/Kernel.cpp | 12 -- src/main.cpp | 76 +----------- src/makefile | 3 - src/modules/tools/endstops/Endstops.cpp | 3 +- 5 files changed, 38 insertions(+), 210 deletions(-) diff --git a/makefile b/makefile index f4dcd9fb..3fbd2885 100644 --- a/makefile +++ b/makefile @@ -1,119 +1,35 @@ -# Select the board that you want to build Smoothie to run upon. -# Can be one of: Smoothie2Proto1 (**current default**) -# Bambino210E -# Bambino200E -BOARD ?= Bambino210E - -# Set MRI_ENABLE to a value of 1 to enable the MRI debug monitor on the secondary UART. -MRI_ENABLE ?= 0 - -# Set MRI_BREAK_ON_INIT to a value of 0 if you don't want MRI to break before running global constructors. -MRI_BREAK_ON_INIT ?= 1 - -# Can set OPTIMIZATION to 0 to disable compiler optimizations. This makes debugging easier but the code will run more -# slowly. It may run so slowly that it fails to execute properly since some routines (ie. step tickers) are -# time sensitive. -#OPTIMIZATION := 0 -OPTIMIZATION := 2 - -# Set to non zero value if you want checks to be enabled which reserve a -# specific amount of space for the stack. The heap's growth will be -# constrained to reserve this much space for the stack and the stack won't be -# able to grow larger than this amount. -#STACK_SIZE=3072 - -# Set to 1 to allow MRI debug monitor to take full control of UART0 and use it -# as a dedicated debug channel. If you are using the USB based serial port for -# the console then this should cause you no problems. Set MRI_BREAK_ON_INIT to -# 0 if you don't want to break into GDB at startup. -ENABLE_DEBUG_MONITOR?=0 - -######################################################################################################################## -PROJECT := Smoothie2 -DEVICES := LPC4330_M4 -GCC4MBED_DIR := ../gcc4mbed/ -NO_FLOAT_SCANF := 1 -NO_FLOAT_PRINTF := 0 -LIBS_PREFIX := configdefault.o -MRI_SEMIHOST_STDIO := 0 -#MBED_LIBS += USBHost -#MBED_LIBS += USBDevice - -# Use C++11 features for the checksums -DEFINES += -DCHECKSUM_USE_CPP - -#Disable the network -DEFINES += -DNONETWORK - -# Need to use a custom LPC4337 script for boards like the Smoothie2 hardware which use that chip instead of the LPC4330. -ifeq "$(BOARD)" "Smoothie2Proto1" -LPC4330_M4_LSCRIPT := LPC4337.ld -endif - -# Select the UART to be used for primary and secondary console (if any). -ifeq "$(BOARD)" "Smoothie2Proto1" -DEFINES += -DSMOOTHIE_UART_PRIMARY_ENABLE=1 -DEFINES += -DSMOOTHIE_UART_PRIMARY_TX=P3_4 -DEFINES += -DSMOOTHIE_UART_PRIMARY_RX=P3_5 -DEFINES += -DSMOOTHIE_UART_SECONDARY_ENABLE=1 -DEFINES += -DSMOOTHIE_UART_SECONDARY_TX=P2_0 -DEFINES += -DSMOOTHIE_UART_SECONDARY_RX=P2_1 -DEFINES += -DTARGET_SMOOTHIE2_PROTO1 -MRI_UART ?= MRI_UART_TX_P2_0 MRI_UART_RX_P2_1 MRI_UART_BAUD=230400 -endif - -ifeq "$(BOARD)" "Bambino210E" -DEFINES += -DSMOOTHIE_UART_PRIMARY_ENABLE=1 -DEFINES += -DSMOOTHIE_UART_PRIMARY_TX=P2_10 -DEFINES += -DSMOOTHIE_UART_PRIMARY_RX=P2_11 -DEFINES += -DSMOOTHIE_UART_SECONDARY_ENABLE=1 -DEFINES += -DSMOOTHIE_UART_SECONDARY_TX=P6_4 -DEFINES += -DSMOOTHIE_UART_SECONDARY_RX=P6_5 -DEFINES += -DTARGET_BAMBINO210E -MRI_UART ?= MRI_UART_TX_P6_4 MRI_UART_RX_P6_5 MRI_UART_BAUD=230400 -endif - -ifeq "$(BOARD)" "Bambino200E" -DEFINES += -DSMOOTHIE_UART_PRIMARY_ENABLE=1 -DEFINES += -DSMOOTHIE_UART_PRIMARY_TX=P6_4 -DEFINES += -DSMOOTHIE_UART_PRIMARY_RX=P6_5 -DEFINES += -DSMOOTHIE_UART_SECONDARY_ENABLE=1 -DEFINES += -DSMOOTHIE_UART_SECONDARY_TX=P5_6 -DEFINES += -DSMOOTHIE_UART_SECONDARY_RX=P1_14 -DEFINES += -DTARGET_BAMBINO200E -MRI_UART ?= MRI_UART_TX_P5_6 MRI_UART_RX_P1_14 MRI_UART_BAUD=230400 -endif - - -# this is the default UART baud rate used if it is not set in config -# it is also the baud rate used to report any errors found while parsing the config file -DEFINES += -DDEFAULT_SERIAL_BAUD_RATE=9600 - -# generate a git version string -ifneq "$(OS)" "Windows_NT" -DEFINES += -D__GITVERSIONSTRING__=\"$(shell ./generate-version.sh)\" -else -DEFINES += -D__GITVERSIONSTRING__=\"$(shell generate-version.bat)\" -endif - -# Disable the secondary console if the MRI debug monitor is enabled so that the secondary UART can be used by -# MRI instead. -ifeq "$(MRI_ENABLE)" "1" -DEFINES := $(subst -DSMOOTHIE_UART_SECONDARY_ENABLE=1,-DSMOOTHIE_UART_SECONDARY_ENABLE=0,$(DEFINES)) -GCC4MBED_TYPE := Checked -else -GCC4MBED_TYPE := Release -endif - - -include $(GCC4MBED_DIR)/build/gcc4mbed.mk - -# set to not compile in any network support -#export NONETWORK = 1 - -configdefault.o : config.default - @echo Packing $< - $(Q) $(OBJCOPY) -I binary -O elf32-littlearm -B arm --readonly-text --rename-section .data=.rodata.configdefault $< $@ - -flash: - lpc21isp LPC4330_M4/Smoothie2.hex /dev/ttyUSB0 115200 14746 + +#DIRS = gcc4mbed src +DIRS = src +DIRSCLEAN = $(addsuffix .clean,$(DIRS)) + +all: +# @ $(MAKE) -C gcc4mbed + @echo Building Smoothie + @ $(MAKE) -C src + +clean: $(DIRSCLEAN) + +$(DIRSCLEAN): %.clean: + @echo Cleaning $* + @ $(MAKE) -C $* clean + +debug-store: + @ $(MAKE) -C src debug-store + +flash: + @ $(MAKE) -C src flash + +dfu: + @ $(MAKE) -C src dfu + +upload: + @ $(MAKE) -C src upload + +debug: + @ $(MAKE) -C src debug + +console: + @ $(MAKE) -C src console + +.PHONY: all $(DIRS) $(DIRSCLEAN) debug-store flash upload debug console dfu diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 0ecf9394..09672821 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -123,14 +123,9 @@ Kernel::Kernel(){ // Configure the step ticker this->base_stepping_frequency = this->config->value(base_stepping_frequency_checksum)->by_default(100000)->as_number(); float microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); -<<<<<<< HEAD this->add_module( this->serial ); -======= - // REMOVE this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(1000)->as_number(); - ->>>>>>> daeb176e8c9c0ece9343444dad27a02445d3c6c2 // HAL stuff add_module( this->slow_ticker = new SlowTicker()); @@ -147,7 +142,6 @@ Kernel::Kernel(){ // Set other priorities lower than the timers NVIC_SetPriority(ADC0_IRQn, 5); NVIC_SetPriority(ADC1_IRQn, 5); -<<<<<<< HEAD // NVIC_SetPriority(USB_IRQn, 5); // // // If MRI is enabled @@ -162,8 +156,6 @@ Kernel::Kernel(){ // NVIC_SetPriority(UART2_IRQn, 5); // NVIC_SetPriority(UART3_IRQn, 5); // } -======= ->>>>>>> daeb176e8c9c0ece9343444dad27a02445d3c6c2 // Configure the step ticker this->step_ticker->set_frequency( this->base_stepping_frequency ); @@ -176,11 +168,7 @@ Kernel::Kernel(){ // TOADDBACK this->add_module( this->simpleshell = new SimpleShell() ); this->planner = new Planner(); -<<<<<<< HEAD this->configurator = new Configurator(); -======= - // TOADDBACK this->configurator = new Configurator(); ->>>>>>> daeb176e8c9c0ece9343444dad27a02445d3c6c2 } // return a GRBL-like query string for serial ? diff --git a/src/main.cpp b/src/main.cpp index d2df4f84..bd8b0cbf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,6 @@ #include "libs/Kernel.h" #include "modules/tools/laser/Laser.h" -<<<<<<< HEAD #include "modules/tools/spindle/Spindle.h" #include "modules/tools/extruder/ExtruderMaker.h" #include "modules/tools/temperaturecontrol/TemperatureControlPool.h" @@ -31,15 +30,6 @@ #include "modules/utils/PlayLed/PlayLed.h" //#include "modules/utils/panel/Panel.h" //#include "libs/Network/uip/Network.h" -======= -#include "modules/tools/extruder/ExtruderMaker.h" -#include "modules/tools/temperaturecontrol/TemperatureControlPool.h" -#include "modules/tools/endstops/Endstops.h" -#include "modules/tools/switch/SwitchPool.h" -#include "modules/tools/drillingcycles/Drillingcycles.h" - -#include "modules/robot/Conveyor.h" ->>>>>>> daeb176e8c9c0ece9343444dad27a02445d3c6c2 #include "Config.h" #include "checksumm.h" #include "ConfigValue.h" @@ -63,7 +53,6 @@ */ #include "StreamOutputPool.h" #include "ToolManager.h" -<<<<<<< HEAD #include "SEGGER_SYSVIEW.h" #include "version.h" @@ -74,14 +63,6 @@ #define disable_msd_checksum CHECKSUM("msd_disable") #define dfu_enable_checksum CHECKSUM("dfu_enable") #define watchdog_timeout_checksum CHECKSUM("watchdog_timeout") -======= - -#include "version.h" -#include "system_LPC43xx.h" -#include "platform_memory.h" - -#include "mbed.h" ->>>>>>> daeb176e8c9c0ece9343444dad27a02445d3c6c2 DigitalOut leds[4] = { #ifdef TARGET_BAMBINO210E @@ -116,7 +97,6 @@ USBMSD *msc= NULL; */ void init() { -<<<<<<< HEAD SEGGER_SYSVIEW_Conf(); // Kernel creates modules, and receives and dispatches events between them @@ -246,59 +226,6 @@ bool sdok= false; //TODO remove once SD Card code is working // kernel->add_module( &u ); -======= - // Kernel creates modules, and receives and dispatches events between them - Kernel* kernel = new Kernel(); - - kernel->streams->printf("Smoothie Running @%ldMHz\r\n", SystemCoreClock / 1000000); - Version version; - kernel->streams->printf(" Build version %s, Build date %s\r\n", version.get_build(), version.get_build_date()); -#ifdef CNC - kernel->streams->printf(" CNC Build\r\n"); -#endif -#ifdef DISABLEMSD - kernel->streams->printf(" NOMSD Build\r\n"); -#endif - - // Create and add main modules - kernel->add_module( new Endstops() ); - kernel->add_module( new Laser() ); - - // Create all Switch modules - SwitchPool *sp= new SwitchPool(); - sp->load_tools(); - delete sp; - - // Create all TemperatureControl modules. Note order is important here must be after extruder so Tn as a parameter will get executed first - TemperatureControlPool *tp= new TemperatureControlPool(); - tp->load_tools(); - delete tp; - - - // TOADDBACK kernel->add_module( &u ); - - - // Clear the configuration cache as it is no longer needed - kernel->config->config_cache_clear(); - - if(kernel->is_using_leds()) { - // set some leds to indicate status... led0 init done, led1 mainloop running, led2 idle loop running, led3 sdcard ok - leds[0]= 0; // indicate we are done with init - // TOADDBACK leds[3]= sdok?1:0; // 4th led indicates sdcard is available (TODO maye should indicate config was found) - } - - // start the timers and interrupts - kernel->conveyor->start(THEROBOT->get_number_registered_motors()); - kernel->step_ticker->start(); - THEKERNEL->slow_ticker->start(); -} - -int main() { - - init(); - - uint16_t cnt = 0; ->>>>>>> daeb176e8c9c0ece9343444dad27a02445d3c6c2 // memory before cache is cleared //SimpleShell::print_mem(kernel->streams); @@ -344,11 +271,10 @@ int main() while(1){ if(THEKERNEL->is_using_leds()) { // flash led 2 to show we are alive - leds[1]= (cnt++ & 0x1000) ? 1 : 0; + leds[0]= (cnt++ & 0x1000) ? 1 : 0; } THEKERNEL->call_event(ON_MAIN_LOOP); THEKERNEL->call_event(ON_IDLE); } } - diff --git a/src/makefile b/src/makefile index 0d4e76b8..f4dcd9fb 100644 --- a/src/makefile +++ b/src/makefile @@ -84,14 +84,11 @@ DEFINES += -DTARGET_BAMBINO200E MRI_UART ?= MRI_UART_TX_P5_6 MRI_UART_RX_P1_14 MRI_UART_BAUD=230400 endif -<<<<<<< HEAD # this is the default UART baud rate used if it is not set in config # it is also the baud rate used to report any errors found while parsing the config file DEFINES += -DDEFAULT_SERIAL_BAUD_RATE=9600 -======= ->>>>>>> daeb176e8c9c0ece9343444dad27a02445d3c6c2 # generate a git version string ifneq "$(OS)" "Windows_NT" DEFINES += -D__GITVERSIONSTRING__=\"$(shell ./generate-version.sh)\" diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index adebfedd..d4242535 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -140,6 +140,7 @@ void Endstops::on_module_loaded() return; } + register_for_event(ON_IDLE); register_for_event(ON_GCODE_RECEIVED); register_for_event(ON_GET_PUBLIC_DATA); register_for_event(ON_SET_PUBLIC_DATA); @@ -431,7 +432,7 @@ uint32_t Endstops::read_endstops(uint32_t dummy) if(debounce[m] < debounce_ms) { debounce[m]++; } else { - // we signal all the motors to stop, as on corexy X and Y motors will move for X and Y axis homing and we only hom eone axis at a time + // we signal all the motors to stop, as on corexy X and Y motors will move for X and Y axis homing and we only home one axis at a time STEPPER[X_AXIS]->stop_moving(); STEPPER[Y_AXIS]->stop_moving(); STEPPER[Z_AXIS]->stop_moving(); From 393f1f9ac49d9535537f77fa7d7723c3d3c55c9a Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sat, 12 Nov 2016 18:31:53 +1300 Subject: [PATCH 62/66] modified: modules/tools/endstops/Endstops.cpp --- src/modules/tools/endstops/Endstops.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index d4242535..eb10cd54 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -140,7 +140,6 @@ void Endstops::on_module_loaded() return; } - register_for_event(ON_IDLE); register_for_event(ON_GCODE_RECEIVED); register_for_event(ON_GET_PUBLIC_DATA); register_for_event(ON_SET_PUBLIC_DATA); From d8bdb49c47f28a418d89bbf6064c59fbe29b8d7b Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 13 Nov 2016 23:05:07 +1300 Subject: [PATCH 63/66] modified: src/config.default --- src/config.default | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/config.default b/src/config.default index f90486e7..09f5a951 100644 --- a/src/config.default +++ b/src/config.default @@ -100,7 +100,6 @@ gamma_dir_pin 1.7 # Pin for gamma st gamma_en_pin 6.9 # Pin for gamma enable gamma_current 1.5 # Z stepper motor current gamma_max_rate 60000.0 # mm/min -<<<<<<< HEAD #SilentStepStick TMC2130 setup motor_driver_control.alpha.enable true # alpha (X) is a TMC2130 @@ -113,7 +112,7 @@ motor_driver_control.alpha.alarm true # set to true mean motor_driver_control.alpha.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set motor_driver_control.alpha.spi_channel 0 # SPI channel 1 is sdcard channel motor_driver_control.alpha.spi_cs_pin 7.4 # SPI CS pin -#motor_driver_control.alpha.spi_frequency 1000000 # SPI frequency +#motor_driver_control.alpha.spi_frequency 1000000 # SPI frequency motor_driver_control.beta.enable true # beta (Y) is a TMC2130 motor_driver_control.beta.designator B # B to set the settings @@ -125,7 +124,7 @@ motor_driver_control.beta.alarm true # set to true mean motor_driver_control.beta.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set motor_driver_control.beta.spi_channel 0 # SPI channel 1 is sdcard channel motor_driver_control.beta.spi_cs_pin 7.5 # SPI CS pin -#motor_driver_control.beta.spi_frequency 1000000 # SPI frequency +#motor_driver_control.beta.spi_frequency 1000000 # SPI frequency motor_driver_control.gamma.enable true # gamma (Z) is a TMC2130 motor_driver_control.gamma.designator C # C to set the settings @@ -137,10 +136,7 @@ motor_driver_control.gamma.alarm true # set to true mean motor_driver_control.gamma.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set motor_driver_control.gamma.spi_channel 0 # SPI channel 1 is sdcard channel ??? motor_driver_control.gamma.spi_cs_pin 1.0 # SPI CS pin -#motor_driver_control.gamma.spi_frequency 1000000 # SPI frequency - -======= ->>>>>>> master +#motor_driver_control.gamma.spi_frequency 1000000 # SPI frequency ## System configuration # Serial communications configuration ( baud rate defaults to 9600 if undefined ) @@ -261,7 +257,7 @@ rdelta_homing true # forces all three alpha_min_endstop 7.4!^ # alpha_max_endstop 7.7^ # add ! to invert pullup if switch is NO to ground alpha_homing_direction home_to_max # Home up -<<<<<<< HEAD + alpha_max 300 # beta_min_endstop 7.5!^ # beta_max_endstop 4.3^ # @@ -271,35 +267,19 @@ gamma_min_endstop 4.1!^ # gamma_max_endstop 4.4^ # gamma_homing_direction home_to_max # gamma_max 300 # -======= -alpha_max 200 # -beta_min_endstop 7.5!^ # -beta_max_endstop 4.3^ # -beta_homing_direction home_to_max # -beta_max 200 # -gamma_min_endstop 4.1!^ # -gamma_max_endstop 4.4^ # -gamma_homing_direction home_to_max # -gamma_max 200 # ->>>>>>> master # optional enable limit switches, actions will stop if any enabled limit switch is triggered -alpha_limit_enable false # set to true to enable X min and max limit switches -beta_limit_enable false # set to true to enable Y min and max limit switches -gamma_limit_enable false # set to true to enable Z min and max limit switches +alpha_limit_enable true # set to true to enable X min and max limit switches +beta_limit_enable true # set to true to enable Y min and max limit switches +gamma_limit_enable true # set to true to enable Z min and max limit switches alpha_fast_homing_rate_mm_s 500 # feedrates in mm/second beta_fast_homing_rate_mm_s 500 # " gamma_fast_homing_rate_mm_s 500 # " -<<<<<<< HEAD -alpha_slow_homing_rate_mm_s 500 # " -beta_slow_homing_rate_mm_s 500 # " -gamma_slow_homing_rate_mm_s 500 # " -======= + alpha_slow_homing_rate_mm_s 250 # " beta_slow_homing_rate_mm_s 250 # " gamma_slow_homing_rate_mm_s 250 # " ->>>>>>> master alpha_homing_retract_mm 5 # distance in mm beta_homing_retract_mm 5 # " @@ -321,5 +301,5 @@ zprobe.probe_height 15 # how much above b # DistanceProbes distanceprobes_enable true -rdelta_homing true # forces all three axis to home a the same time regardless of +#rdelta_homing true # forces all three axis to home a the same time regardless of From 3a8459b631b4fc32c40f988d947ad885c8265b90 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 27 Nov 2016 15:32:17 +1300 Subject: [PATCH 64/66] modified: src/config.default new file: src/copy.default.rotarydelta.default modified: src/libs/Kernel.cpp modified: src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp modified: src/modules/tools/endstops/Endstops.cpp --- src/config.default | 24 +- src/copy.default.rotarydelta.default | 305 ++++++++++++++++++ src/libs/Kernel.cpp | 2 +- .../arm_solutions/RotaryDeltaSolution.cpp | 9 +- src/modules/tools/endstops/Endstops.cpp | 55 +++- 5 files changed, 369 insertions(+), 26 deletions(-) create mode 100644 src/copy.default.rotarydelta.default diff --git a/src/config.default b/src/config.default index 09f5a951..b513db15 100644 --- a/src/config.default +++ b/src/config.default @@ -84,22 +84,22 @@ gamma_steps_per_mm 2708.554567 # Steps per mm for # Stepper module pins ( ports, and pin numbers, appending "!" to the number will invert a pin ) alpha_step_pin 5.0 # Pin for alpha stepper step signal -alpha_dir_pin 6.12 # Pin for alpha stepper direction +alpha_dir_pin 6.12! # Pin for alpha stepper direction alpha_en_pin 4.10 # Pin for alpha enable pin alpha_current 1.5 # X stepper motor current -alpha_max_rate 60000.0 # mm/min +alpha_max_rate 600.0 # mm/min beta_step_pin 5.7 # Pin for beta stepper step signal -beta_dir_pin 5.5 # Pin for beta stepper direction +beta_dir_pin 5.5! # Pin for beta stepper direction beta_en_pin 7.6 # Pin for beta enable beta_current 1.5 # Y stepper motor current -beta_max_rate 60000.0 # mm/min +beta_max_rate 600.0 # mm/min gamma_step_pin 4.0 # Pin for gamma stepper step signal -gamma_dir_pin 1.7 # Pin for gamma stepper direction +gamma_dir_pin 1.7! # Pin for gamma stepper direction gamma_en_pin 6.9 # Pin for gamma enable gamma_current 1.5 # Z stepper motor current -gamma_max_rate 60000.0 # mm/min +gamma_max_rate 600.0 # mm/min #SilentStepStick TMC2130 setup motor_driver_control.alpha.enable true # alpha (X) is a TMC2130 @@ -277,9 +277,9 @@ alpha_fast_homing_rate_mm_s 500 # feedrates in mm/ beta_fast_homing_rate_mm_s 500 # " gamma_fast_homing_rate_mm_s 500 # " -alpha_slow_homing_rate_mm_s 250 # " -beta_slow_homing_rate_mm_s 250 # " -gamma_slow_homing_rate_mm_s 250 # " +alpha_slow_homing_rate_mm_s 50 # " +beta_slow_homing_rate_mm_s 50 # " +gamma_slow_homing_rate_mm_s 50 # " alpha_homing_retract_mm 5 # distance in mm beta_homing_retract_mm 5 # " @@ -293,10 +293,10 @@ gamma_trim 0 # software trim fo # optional Z probe zprobe.enable true # set to true to enable a zprobe -#zprobe.probe_pin 4.4!^ # pin probe is attached to if NC remove the ! -zprobe.slow_feedrate 5 # mm/sec probe feed rate +zprobe.probe_pin 7.3^ # pin probe is attached to if NC remove the ! +zprobe.slow_feedrate 50 #5 # mm/sec probe feed rate #zprobe.debounce_count 100 # set if noisy -zprobe.fast_feedrate 100 # move feedrate mm/sec +zprobe.fast_feedrate 5000 # move feedrate mm/sec zprobe.probe_height 15 # how much above bed to start probe # DistanceProbes diff --git a/src/copy.default.rotarydelta.default b/src/copy.default.rotarydelta.default new file mode 100644 index 00000000..e46aa2d2 --- /dev/null +++ b/src/copy.default.rotarydelta.default @@ -0,0 +1,305 @@ +# NOTE Lines must not exceed 132 characters +# Robot module configurations : general handling of movement G-codes and slicing into moves +default_feed_rate 4000 # Default rate ( mm/minute ) for G1/G2/G3 moves +default_seek_rate 4000 # Default rate ( mm/minute ) for G0 moves +mm_per_arc_segment 0.0 # Fixed length for line segments that divide arcs 0 to disable +mm_max_arc_error 0.01 # The maximum error for line segments that divide arcs 0 to disable + # note it is invalid for both the above be 0 + # if both are used, will use largest segment length based on radius +#mm_per_line_segment 0.5 # Lines can be cut into segments ( not useful with cartesian + # coordinates robots ). +delta_segments_per_second 100 # for deltas only same as in Marlin/Delta, set to 0 to disable + # and use mm_per_line_segment + +# Arm solution configuration : Cartesian robot. Translates mm positions into stepper positions +#alpha_steps_per_mm 80 # Steps per mm for alpha stepper +#beta_steps_per_mm 80 # Steps per mm for beta stepper +#gamma_steps_per_mm 80 # Steps per mm for gamma stepper + +# Planner module configuration : Look-ahead and acceleration configuration +planner_queue_size 32 # DO NOT CHANGE THIS UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING +acceleration 3000 # Acceleration in mm/second/second. +#z_acceleration 500 # Acceleration for Z only moves in mm/s^2, 0 uses acceleration which is the default. DO NOT SET ON A DELTA +acceleration_ticks_per_second 1000 # Number of times per second the speed is updated +junction_deviation 0.05 # Similar to the old "max_jerk", in millimeters, + # see https://github.com/grbl/grbl/blob/master/planner.c + # and https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.8 + # Lower values mean being more careful, higher values means being + # faster and have more jerk +#z_junction_deviation 0.0 # for Z only moves, -1 uses junction_deviation, zero disables junction_deviation on z moves DO NOT SET ON A DELTA +#minimum_planner_speed 0.0 # sets the minimum planner speed in mm/sec + +# Stepper module configuration +microseconds_per_step_pulse 5 # Duration of step pulses to stepper drivers, in microseconds +base_stepping_frequency 100000 # Base frequency for stepping + +# Cartesian axis speed limits +x_axis_max_speed 60000 # mm/min +y_axis_max_speed 60000 # mm/min +z_axis_max_speed 60000 # mm/min + +# Arm solution configuration : Rotatable Delta robot. Translates mm positions into stepper positions +arm_solution rotary_delta # selects the delta arm solution + +delta_e 131.636 # End effector length, triangle side length in mm +delta_f 190.526 # Base length, triangle side length in mm +delta_re 270.000 # Carbon rod length +delta_rf 90.000 # Servo horn length + +delta_z_offset 268.0 # Distance from big pulley shaft to table/bed +delta_ee_offs 15.000 # Ball joint plane to bottom of end effector surface +delta_tool_offset 30.500 # Distance between end effector ball joint plane and tip of tool (PnP) + +delta_mirror_xy true # true for firepick + +# For a FirePick Rotary Delta, the steps per degree are calculated as: +# +# (1) First determine the circumference of the big pulley, which has a smooth surface, and calculate the 'teeth it would have' +# (2) Determine the GT2 belt thickness and tooth depth +# (3) Calculate the ACTUAL big pulley circumference taking into account that the GT2 belt affects this as +# it rests on the big pulley and not into teeth cut into the pulley, the formula is: +# BIG_PULLEY_CIRCUMFERENCE = BIG_PULLEY_TEETH * TOOTH_SPACING + (BELT_THICKNESS - TOOTH_DEPTH)*6.283185 +# (4) Calculate the ACTUAL small pulley circumference taking into account that the GT2 belt affects this as +# rests into the small pulley as it has teeth cut into the pulley, the formula is: +# SMALL_PULLEY_CIRCUMFERENCE = SMALL_PULLEY_TEETH * TOOTH_SPACING +# (5) Now determine the pulley reduction which is: +# PULLEY_REDUCTION = BIG_PULLEY_CIRCUMFERENCE / SMALL_PULLEY_CIRCUMFERENCE +# (6) Finally, given the number of steps a stepper motor needs for a full 360 degree rotation, and the number of microsteps your +# stepper motor driver chip is set for, you can calculate the STEPS_PER_MM for each axis, which for a Rotary Delta, are the same: +# [ALPHA/BETA/GAMMA]_STEPS_PER_MM = (XYZ_STEPS_PER_ROTATION*XYZ_MICROSTEPS*PULLEY_REDUCTION)/360 +# e.g. a FirePick Delta mechanism, we can calculate: +# BIG_PULLEY_CIRCUMFERENCE = 150 * 2 + (1.49 - 0.74) * 6.283185 = 304.712389 +# SMALL_PULLEY_CIRCUMFERENCE = 16 * 2 = 32 +# PULLEY_REDUCTION = 304.712389 / 32 = 9.52226215 +# For a 0.9 degree stepper motor and an a4988 driver set for 16 microsteps = (400 * 16 * 9.52226215)/360 = 169.2846604 +# For a 1.8 degree stepper motor and an a4988 driver set for 16 microsteps = (200 * 16 * 9.52226215)/360 = 84.6423302 +# For a 0.9 degree stepper motor and an drv8825 driver set for 32 microsteps = (400 * 32 * 9.52226215)/360 = 338.5693208 +# For a 1.8 degree stepper motor and an drv8825 driver set for 32 microsteps = (200 * 32 * 9.52226215)/360 = 169.2846604 +# For a 0.9 degree stepper motor and an TMC2130 driver set for 256 microsteps = (400 * 256 * 9.52226215)/360 = 2708.554567 +# For a 1.8 degree stepper motor and an TMC2130 driver set for 256 microsteps = (200 * 256 * 9.52226215)/360 = 1354.277284 + +alpha_steps_per_mm 2708.554567 # Steps per mm for alpha stepper +beta_steps_per_mm 2708.554567 # Steps per mm for beta stepper +gamma_steps_per_mm 2708.554567 # Steps per mm for gamma stepper + +# Stepper module pins ( ports, and pin numbers, appending "!" to the number will invert a pin ) +alpha_step_pin 5.0 # Pin for alpha stepper step signal +alpha_dir_pin 6.12 # Pin for alpha stepper direction +alpha_en_pin 4.10 # Pin for alpha enable pin +alpha_current 1.5 # X stepper motor current +alpha_max_rate 60000.0 # mm/min + +beta_step_pin 5.7 # Pin for beta stepper step signal +beta_dir_pin 5.5 # Pin for beta stepper direction +beta_en_pin 7.6 # Pin for beta enable +beta_current 1.5 # Y stepper motor current +beta_max_rate 60000.0 # mm/min + +gamma_step_pin 4.0 # Pin for gamma stepper step signal +gamma_dir_pin 1.7 # Pin for gamma stepper direction +gamma_en_pin 6.9 # Pin for gamma enable +gamma_current 1.5 # Z stepper motor current +gamma_max_rate 60000.0 # mm/min + +#SilentStepStick TMC2130 setup +motor_driver_control.alpha.enable true # alpha (X) is a TMC2130 +motor_driver_control.alpha.designator A # A to set the settings +motor_driver_control.alpha.chip TMC2130 # chip name +motor_driver_control.alpha.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.alpha.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.alpha.microsteps 256 # microsteps +motor_driver_control.alpha.alarm true # set to true means the error bits are checked +motor_driver_control.alpha.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set +motor_driver_control.alpha.spi_channel 0 # SPI channel 1 is sdcard channel +motor_driver_control.alpha.spi_cs_pin 7.4 # SPI CS pin +#motor_driver_control.alpha.spi_frequency 1000000 # SPI frequency + +motor_driver_control.beta.enable true # beta (Y) is a TMC2130 +motor_driver_control.beta.designator B # B to set the settings +motor_driver_control.beta.chip TMC2130 # chip name +motor_driver_control.beta.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.beta.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.beta.microsteps 256 # microsteps +motor_driver_control.beta.alarm true # set to true means the error bits are checked +motor_driver_control.beta.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set +motor_driver_control.beta.spi_channel 0 # SPI channel 1 is sdcard channel +motor_driver_control.beta.spi_cs_pin 7.5 # SPI CS pin +#motor_driver_control.beta.spi_frequency 1000000 # SPI frequency + +motor_driver_control.gamma.enable true # gamma (Z) is a TMC2130 +motor_driver_control.gamma.designator C # C to set the settings +motor_driver_control.gamma.chip TMC2130 # chip name +motor_driver_control.gamma.current 31 # current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.gamma.max_current 31 # max current in 0..31 representing a ratio of 1/32 .. 32/32 +motor_driver_control.gamma.microsteps 256 # microsteps +motor_driver_control.gamma.alarm true # set to true means the error bits are checked +motor_driver_control.gamma.halt_on_alarm false # if set to true means ON_HALT is entered on any error bits being set +motor_driver_control.gamma.spi_channel 0 # SPI channel 1 is sdcard channel ??? +motor_driver_control.gamma.spi_cs_pin 1.0 # SPI CS pin +#motor_driver_control.gamma.spi_frequency 1000000 # SPI frequency + +## System configuration +# Serial communications configuration ( baud rate defaults to 9600 if undefined ) +uart0.baud_rate 115200 # Baud rate for the default hardware serial port +second_usb_serial_enable false # This enables a second usb serial port (to have both pronterface + # and a terminal connected) +#leds_disable true # disable using leds after config loaded +#play_led_disable true # disable the play led + +# Kill button (used to be called pause) maybe assigned to a different pin, set to the onboard pin by default +kill_button_enable false # set to true to enable a kill button +kill_button_pin 2.12 # kill button pin. default is same as pause button 2.12 (2.11 is another good choice) + +#msd_disable false # disable the MSD (USB SDCARD) when set to true (needs special binary) +#dfu_enable false # for linux developers, set to true to enable DFU +#watchdog_timeout 10 # watchdog timeout in seconds, default is 10, set to 0 to disable the watchdog + +# Only needed on a smoothieboard +currentcontrol_module_enable false # + +## Extruder module configuration +extruder.hotend.enable false # Whether to activate the extruder module at all. All configuration is ignored if false +extruder.hotend.steps_per_mm 140 # Steps per mm for extruder stepper +extruder.hotend.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves +extruder.hotend.acceleration 500 # Acceleration for the stepper motor mm/sec² +extruder.hotend.max_speed 50 # mm/s + +extruder.hotend.step_pin 6.4 # Pin for extruder step signal +extruder.hotend.dir_pin 6.5 # Pin for extruder dir signal +extruder.hotend.en_pin 1.7 # Pin for extruder enable signal + + +# Second extruder module configuration +extruder.hotend2.enable false # Whether to activate the extruder module at all. All configuration is ignored if false +extruder.hotend2.steps_per_mm 140 # Steps per mm for extruder stepper +extruder.hotend2.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves +extruder.hotend2.acceleration 500 # Acceleration for the stepper motor, as of 0.6, arbitrary ratio +extruder.hotend2.max_speed 50 # mm/s + +extruder.hotend2.step_pin 6.10 # Pin for extruder step signal +extruder.hotend2.dir_pin 2.9 # Pin for extruder dir signal +extruder.hotend2.en_pin 2.8 # Pin for extruder enable signal + + +## Laser module configuration +laser_module_enable false # Whether to activate the laser module at all. All configuration is + # ignored if false. +## Temperature control configuration +# First hotend configuration +temperature_control.hotend.enable false # Whether to activate this ( "hotend" ) module at all. + # All configuration is ignored if false. +temperature_control.hotend.thermistor_pin adc.5 # Pin for the thermistor to read +temperature_control.hotend.heater_pin 7.5 # Pin that controls the heater, set to nc if a readonly thermistor is being defined +temperature_control.hotend.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5 +#temperature_control.hotend.beta 4066 # or set the beta value +temperature_control.hotend.set_m_code 104 # +temperature_control.hotend.set_and_wait_m_code 109 # +temperature_control.hotend.designator T # +#temperature_control.hotend.max_temp 300 # Set maximum temperature - Will prevent heating above 300 by default +#temperature_control.hotend.min_temp 0 # Set minimum temperature - Will prevent heating below if set + +#temperature_control.hotend.p_factor 13.7 # permanently set the PID values after an auto pid +#temperature_control.hotend.i_factor 0.097 # +#temperature_control.hotend.d_factor 24 # + +#temperature_control.hotend.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v. + +# Second hotend configuration +temperature_control.hotend2.enable false # Whether to activate this ( "hotend" ) module at all. + # All configuration is ignored if false. + +temperature_control.hotend2.thermistor_pin adc.7 # Pin for the thermistor to read +temperature_control.hotend2.heater_pin 7.7 # Pin that controls the heater +temperature_control.hotend2.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5 +#temperature_control.hotend2.beta 4066 # or set the beta value +temperature_control.hotend2.set_m_code 104 # +temperature_control.hotend2.set_and_wait_m_code 109 # +temperature_control.hotend2.designator T1 # + +#temperature_control.hotend2.p_factor 13.7 # permanently set the PID values after an auto pid +#temperature_control.hotend2.i_factor 0.097 # +#temperature_control.hotend2.d_factor 24 # + +#temperature_control.hotend2.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v. + +temperature_control.bed.enable false # +temperature_control.bed.thermistor_pin adc.2 # +temperature_control.bed.heater_pin 7.7 # +temperature_control.bed.thermistor Honeywell100K # see http://smoothieware.org/temperaturecontrol#toc5 +#temperature_control.bed.beta 3974 # or set the beta value + +temperature_control.bed.set_m_code 140 # +temperature_control.bed.set_and_wait_m_code 190 # +temperature_control.bed.designator B # + +#temperature_control.bed.bang_bang false # set to true to use bang bang control rather than PID +#temperature_control.bed.hysteresis 2.0 # set to the temperature in degrees C to use as hysteresis + # when using bang bang + +## Switch module for fan control +switch.fan.enable false # +switch.fan.input_on_command M106 # +switch.fan.input_off_command M107 # +switch.fan.output_pin 4.1 # +switch.fan.output_type pwm # pwm output settable with S parameter in the input_on_comand +switch.fan.max_pwm 255 # set max pwm for the pin default is 255 + +switch.misc.enable false # +switch.misc.input_on_command M42 # +switch.misc.input_off_command M43 # +switch.misc.output_pin 7.7 # +switch.misc.output_type pwm # just an on or off pin + +## Endstops +endstops_enable true # the endstop module is enabled by default and can be disabled here +rdelta_homing true # forces all three axis to home a the same time regardless of + # what is specified in G28 +alpha_min_endstop 7.4!^ # +alpha_max_endstop 7.7^ # add ! to invert pullup if switch is NO to ground +alpha_homing_direction home_to_max # Home up + +alpha_max 300 # +beta_min_endstop 7.5!^ # +beta_max_endstop 4.3^ # +beta_homing_direction home_to_max # +beta_max 300 # +gamma_min_endstop 4.1!^ # +gamma_max_endstop 4.4^ # +gamma_homing_direction home_to_max # +gamma_max 300 # + +# optional enable limit switches, actions will stop if any enabled limit switch is triggered +alpha_limit_enable true # set to true to enable X min and max limit switches +beta_limit_enable true # set to true to enable Y min and max limit switches +gamma_limit_enable true # set to true to enable Z min and max limit switches + +alpha_fast_homing_rate_mm_s 500 # feedrates in mm/second +beta_fast_homing_rate_mm_s 500 # " +gamma_fast_homing_rate_mm_s 500 # " + +alpha_slow_homing_rate_mm_s 50 # " +beta_slow_homing_rate_mm_s 50 # " +gamma_slow_homing_rate_mm_s 50 # " + +alpha_homing_retract_mm 5 # distance in mm +beta_homing_retract_mm 5 # " +gamma_homing_retract_mm 5 # " + +endstop_debounce_count 100 # uncomment if you get noise on your endstops, default is 100 + +alpha_trim 0 # software trim for alpha stepper endstop (in mm) +beta_trim 0 # software trim for beta stepper endstop (in mm) +gamma_trim 0 # software trim for gamma stepper endstop (in mm) + +# optional Z probe +zprobe.enable true # set to true to enable a zprobe +#zprobe.probe_pin 4.4!^ # pin probe is attached to if NC remove the ! +zprobe.slow_feedrate 5 # mm/sec probe feed rate +#zprobe.debounce_count 100 # set if noisy +zprobe.fast_feedrate 100 # move feedrate mm/sec +zprobe.probe_height 15 # how much above bed to start probe + +# DistanceProbes +distanceprobes_enable true +#rdelta_homing true # forces all three axis to home a the same time regardless of + diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 09672821..71c85911 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -136,7 +136,7 @@ Kernel::Kernel(){ NVIC_SetPriorityGrouping(0); NVIC_SetPriority(TIMER0_IRQn, 2); NVIC_SetPriority(TIMER1_IRQn, 1); - NVIC_SetPriority(TIMER2_IRQn, 4); + NVIC_SetPriority(TIMER2_IRQn, 1); NVIC_SetPriority(PendSV_IRQn, 3); // Set other priorities lower than the timers diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp index fe9aeed9..37b903e2 100755 --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp @@ -154,9 +154,12 @@ void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu if (status == -1) { //something went wrong, //force to actuator FPD home position as we know this is a valid position - actuator_mm[ALPHA_STEPPER] = 0; - actuator_mm[BETA_STEPPER ] = 0; - actuator_mm[GAMMA_STEPPER] = 0; + //TODO REwork this as forcing home is bad if the mechanics are not calibrate; we could crash into something + //ideally we should return a bad return-code so the calling logic knows it has asked this arm solution + //to do something it cannot achieve - Douglas Pearless 2016-11-27 +// actuator_mm[ALPHA_STEPPER] = 0; +// actuator_mm[BETA_STEPPER ] = 0; +// actuator_mm[GAMMA_STEPPER] = 0; //DEBUG CODE, uncomment the following to help determine what may be happening if you are trying to adapt this to your own different roational delta. if(debug_flag) { diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index eb10cd54..c4132b6f 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -125,6 +125,12 @@ enum { LIMIT_TRIGGERED }; +//TODO remove debug +DigitalOut LED_1 = DigitalOut(P4_5) = 0; //aka D27 +DigitalOut LED_alpha = DigitalOut(P4_2) = 0; //aka D26 Alpha Endstop +DigitalOut LED_beta = DigitalOut(P7_0) = 0; //aka D25 Beta Endstop +DigitalOut LED_gamma = DigitalOut(P7_1) = 0; //aka D24 Gamma Endstop + Endstops::Endstops() { this->status = NOT_HOMING; @@ -147,7 +153,7 @@ void Endstops::on_module_loaded() // Settings this->load_config(); - THEKERNEL->slow_ticker->attach(1000, this, &Endstops::read_endstops); + THEKERNEL->slow_ticker->attach(10000, this, &Endstops::read_endstops); } // Get config @@ -400,14 +406,26 @@ void Endstops::move_to_origin(std::bitset<3> axis) // Called every millisecond in an ISR uint32_t Endstops::read_endstops(uint32_t dummy) { - if(this->status != MOVING_TO_ENDSTOP_SLOW && this->status != MOVING_TO_ENDSTOP_FAST) return 0; // not doing anything we need to monitor for + LED_1 = 1; //TODO remove debug + bool endstop_triggered = false; + if(this->status != MOVING_TO_ENDSTOP_SLOW && this->status != MOVING_TO_ENDSTOP_FAST) + { + LED_1 = 0; //TODO remove debug + return 0; // not doing anything we need to monitor for + } if(!is_corexy) { // check each axis for ( int m = X_AXIS; m <= Z_AXIS; m++ ) { if(STEPPER[m]->is_moving()) { // if it is moving then we check the associated endstop, and debounce it - if(this->pins[m + (this->home_direction[m] ? 0 : 3)].get()) { + //if(this->pins[m + (this->home_direction[m] ? 0 : 3)].get()) { + int n = m + (this->home_direction[m] ? 0 : 3); + endstop_triggered = this->pins[n].get(); + if(endstop_triggered) { + if (m==0) LED_alpha = 1; //TODO remove debug + if (m==1) LED_beta = 1; //TODO remove debug + if (m==2) LED_gamma = 1; //TODO remove debug if(debounce[m] < debounce_ms) { debounce[m]++; } else { @@ -416,6 +434,9 @@ uint32_t Endstops::read_endstops(uint32_t dummy) } } else { + if (m==0) LED_alpha = 0; //TODO remove debug + if (m==1) LED_beta = 0; //TODO remove debug + if (m==2) LED_gamma = 0; //TODO remove debug // The endstop was not hit yet debounce[m] = 0; } @@ -444,7 +465,7 @@ uint32_t Endstops::read_endstops(uint32_t dummy) } } } - + LED_1 = 0; //TODO remove debug return 0; } @@ -480,6 +501,10 @@ void Endstops::home(std::bitset<3> a) // reset debounce counts debounce.fill(0); + LED_alpha = 0; //TODO remove debug + LED_beta = 0; //TODO remove debug + LED_gamma = 0; //TODO remove debug + // turn off any compensation transform auto savect= THEROBOT->compensationTransform; THEROBOT->compensationTransform= nullptr; @@ -492,7 +517,9 @@ void Endstops::home(std::bitset<3> a) THEROBOT->disable_segmentation= true; // we must disable segmentation as this won't work with it enabled if(!home_z_first) home_xy(); - + LED_alpha = 0; //TODO remove debug + LED_beta = 0; //TODO remove debug + LED_gamma = 0; //TODO remove debug if(axis_to_home[Z_AXIS]) { // now home z float delta[3] {0, 0, gamma_max}; // we go the max z @@ -501,9 +528,13 @@ void Endstops::home(std::bitset<3> a) // wait for Z THECONVEYOR->wait_for_idle(); } - + LED_alpha = 0; //TODO remove debug + LED_beta = 0; //TODO remove debug + LED_gamma = 0; //TODO remove debug if(home_z_first) home_xy(); - + LED_alpha = 0; //TODO remove debug + LED_beta = 0; //TODO remove debug + LED_gamma = 0; //TODO remove debug // TODO should check that the endstops were hit and it did not stop short for some reason // we did not complete movement the full distance if we hit the endstops THEROBOT->reset_position_from_current_actuator_position(); @@ -524,7 +555,9 @@ void Endstops::home(std::bitset<3> a) THEROBOT->delta_move(delta, feed_rate, 3); // wait until finished THECONVEYOR->wait_for_idle(); - + LED_alpha = 0; //TODO remove debug + LED_beta = 0; //TODO remove debug + LED_gamma = 0; //TODO remove debug // Start moving the axes towards the endstops slowly this->status = MOVING_TO_ENDSTOP_SLOW; for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { @@ -538,7 +571,9 @@ void Endstops::home(std::bitset<3> a) THEROBOT->delta_move(delta, feed_rate, 3); // wait until finished THECONVEYOR->wait_for_idle(); - + LED_alpha = 0; //TODO remove debug + LED_beta = 0; //TODO remove debug + LED_gamma = 0; //TODO remove debug // TODO should check that the endstops were hit and it did not stop short for some reason // we did not complete movement the full distance if we hit the endstops THEROBOT->reset_position_from_current_actuator_position(); @@ -731,7 +766,7 @@ void Endstops::process_home_command(Gcode* gcode) // NOTE a rotary delta usually has optical or hall-effect endstops so it is safe to go past them a little bit if(this->move_to_origin_after_home) move_to_origin(haxis); // if limit switches are enabled we must back off endstop after setting home - back_off_home(haxis); + back_off_home(haxis); //TODO figure out why this goes in the wrong direction DHP } else if(this->move_to_origin_after_home || this->limit_enable[X_AXIS]) { // deltas are not left at 0,0 because of the trim settings, so move to 0,0 if requested, but we need to back off endstops first From 6cdf11cbaca929604c23d5dfc81594bd17a5be20 Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Sun, 27 Nov 2016 20:41:52 +1300 Subject: [PATCH 65/66] modified: src/modules/robot/Planner.cpp modified: src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp modified: src/modules/robot/arm_solutions/RotaryDeltaSolution.h modified: src/modules/tools/endstops/Endstops.cpp --- src/modules/robot/Planner.cpp | 3 +- .../arm_solutions/RotaryDeltaSolution.cpp | 35 +++++++++++++++---- .../robot/arm_solutions/RotaryDeltaSolution.h | 10 +++--- src/modules/tools/endstops/Endstops.cpp | 2 +- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/modules/robot/Planner.cpp b/src/modules/robot/Planner.cpp index 09ef0a83..1432e44a 100644 --- a/src/modules/robot/Planner.cpp +++ b/src/modules/robot/Planner.cpp @@ -53,8 +53,7 @@ void Planner::config_load() bool Planner::append_block( ActuatorCoordinates &actuator_pos, uint8_t n_motors, float rate_mm_s, float distance, float *unit_vec, float acceleration, float s_value, bool g123) { // Create ( recycle ) a new block - //Block* block = THECONVEYOR->queue.head_ref(); - Block* block = THEKERNEL->conveyor->queue.head_ref(); + Block* block = THECONVEYOR->queue.head_ref(); // Direction bits bool has_steps = false; diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp index 37b903e2..5dd38a6e 100755 --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.cpp @@ -17,7 +17,9 @@ #define delta_z_offset_checksum CHECKSUM("delta_z_offset") #define delta_ee_offs_checksum CHECKSUM("delta_ee_offs") -#define tool_offset_checksum CHECKSUM("tool_offset") +#define tool_offset_checksum CHECKSUM("delta_tool_offset") + +#define delta_mirror_xy_checksum CHECKSUM("delta_mirror_xy") const static float pi = 3.14159265358979323846; // PI const static float two_pi = 2 * pi; @@ -51,6 +53,10 @@ RotaryDeltaSolution::RotaryDeltaSolution(Config *config) // Distance between end effector ball joint plane and tip of tool (PnP) tool_offset = config->value(tool_offset_checksum)->by_default(30.500F)->as_number(); + // mirror the XY axis + mirror_xy= config->value(delta_mirror_xy_checksum)->by_default(true)->as_bool(); + + debug_flag= false; init(); } @@ -130,7 +136,6 @@ int RotaryDeltaSolution::delta_calcForward(float theta1, float theta2, float the void RotaryDeltaSolution::init() { - //these are calculated here and not in the config() as these variables can be fine tuned by the user. z_calc_offset = -(delta_z_offset - tool_offset - delta_ee_offs); } @@ -142,10 +147,16 @@ void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu float beta_theta = 0.0F; float gamma_theta = 0.0F; - //Code from Trossen Robotics tutorial, note we put the X axis at the back and not the front of the robot. + //Code from Trossen Robotics tutorial, has X in front Y to the right and Z to the left + // firepick is X at the back and negates X0 X0 + // selected by a config option + float x0 = cartesian_mm[X_AXIS]; + float y0 = cartesian_mm[Y_AXIS]; + if(mirror_xy) { + x0= -x0; + y0= -y0; + } - float x0 = -cartesian_mm[X_AXIS]; - float y0 = -cartesian_mm[Y_AXIS]; float z_with_offset = cartesian_mm[Z_AXIS] + z_calc_offset; //The delta calculation below places zero at the top. Subtract the Z offset to make zero at the bottom. int status = delta_calcAngleYZ(x0, y0, z_with_offset, alpha_theta); @@ -161,7 +172,7 @@ void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu // actuator_mm[BETA_STEPPER ] = 0; // actuator_mm[GAMMA_STEPPER] = 0; - //DEBUG CODE, uncomment the following to help determine what may be happening if you are trying to adapt this to your own different roational delta. + //DEBUG CODE, uncomment the following to help determine what may be happening if you are trying to adapt this to your own different rotary delta. if(debug_flag) { THEKERNEL->streams->printf("//ERROR: Delta calculation fail! Unable to move to:\n"); THEKERNEL->streams->printf("// x= %f\n", cartesian_mm[X_AXIS]); @@ -190,8 +201,18 @@ void RotaryDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], Actu void RotaryDeltaSolution::actuator_to_cartesian(const ActuatorCoordinates &actuator_mm, float cartesian_mm[] ) { + float x, y, z; //Use forward kinematics - delta_calcForward(actuator_mm[ALPHA_STEPPER], actuator_mm[BETA_STEPPER ], actuator_mm[GAMMA_STEPPER], cartesian_mm[X_AXIS], cartesian_mm[Y_AXIS], cartesian_mm[Z_AXIS]); + delta_calcForward(actuator_mm[ALPHA_STEPPER], actuator_mm[BETA_STEPPER ], actuator_mm[GAMMA_STEPPER], x, y, z); + if(mirror_xy) { + cartesian_mm[X_AXIS]= -x; + cartesian_mm[Y_AXIS]= -y; + cartesian_mm[Z_AXIS]= z; + }else{ + cartesian_mm[X_AXIS]= x; + cartesian_mm[Y_AXIS]= y; + cartesian_mm[Z_AXIS]= z; + } } bool RotaryDeltaSolution::set_optional(const arm_options_t &options) diff --git a/src/modules/robot/arm_solutions/RotaryDeltaSolution.h b/src/modules/robot/arm_solutions/RotaryDeltaSolution.h index dd475f7a..1f226c17 100755 --- a/src/modules/robot/arm_solutions/RotaryDeltaSolution.h +++ b/src/modules/robot/arm_solutions/RotaryDeltaSolution.h @@ -1,5 +1,5 @@ -#ifndef RotaryDeltaSolution_H -#define RotaryDeltaSolution_H +#pragma once + #include "libs/Module.h" #include "BaseSolution.h" @@ -30,6 +30,8 @@ class RotaryDeltaSolution : public BaseSolution { float tool_offset; // Distance between end effector ball joint plane and tip of tool float z_calc_offset; - bool debug_flag{false}; + struct { + bool debug_flag:1; + bool mirror_xy:1; + }; }; -#endif // RotaryDeltaSolution_H diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index c4132b6f..91e42531 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -766,7 +766,7 @@ void Endstops::process_home_command(Gcode* gcode) // NOTE a rotary delta usually has optical or hall-effect endstops so it is safe to go past them a little bit if(this->move_to_origin_after_home) move_to_origin(haxis); // if limit switches are enabled we must back off endstop after setting home - back_off_home(haxis); //TODO figure out why this goes in the wrong direction DHP + back_off_home(haxis); } else if(this->move_to_origin_after_home || this->limit_enable[X_AXIS]) { // deltas are not left at 0,0 because of the trim settings, so move to 0,0 if requested, but we need to back off endstops first From a83b322c4f967223f50b82b45d72423fbcae2d4c Mon Sep 17 00:00:00 2001 From: Douglas Pearless Date: Thu, 1 Dec 2016 21:27:51 +1300 Subject: [PATCH 66/66] modified: src/modules/robot/Block.h modified: src/modules/robot/Conveyor.h modified: src/modules/tools/endstops/Endstops.cpp modified: src/modules/tools/endstops/Endstops.h modified: src/modules/tools/zprobe/ZProbe.cpp --- src/modules/robot/Block.h | 6 +- src/modules/robot/Conveyor.h | 6 +- src/modules/tools/endstops/Endstops.cpp | 96 +++---------------------- src/modules/tools/endstops/Endstops.h | 6 +- src/modules/tools/zprobe/ZProbe.cpp | 4 +- 5 files changed, 16 insertions(+), 102 deletions(-) diff --git a/src/modules/robot/Block.h b/src/modules/robot/Block.h index da5c1996..3df796ce 100644 --- a/src/modules/robot/Block.h +++ b/src/modules/robot/Block.h @@ -5,8 +5,7 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef BLOCK_H -#define BLOCK_H +#pragma once #include #include @@ -78,6 +77,3 @@ class Block { uint16_t s_value:12; // for laser 1.11 Fixed point }; }; - - -#endif diff --git a/src/modules/robot/Conveyor.h b/src/modules/robot/Conveyor.h index 9ea8e40a..ba2b09a7 100644 --- a/src/modules/robot/Conveyor.h +++ b/src/modules/robot/Conveyor.h @@ -5,8 +5,7 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef CONVEYOR_H -#define CONVEYOR_H +#pragma once #include "libs/Module.h" #include "HeapRing.h" @@ -64,6 +63,3 @@ class Conveyor : public Module }; }; - - -#endif // CONVEYOR_H diff --git a/src/modules/tools/endstops/Endstops.cpp b/src/modules/tools/endstops/Endstops.cpp index 91e42531..64add9d9 100644 --- a/src/modules/tools/endstops/Endstops.cpp +++ b/src/modules/tools/endstops/Endstops.cpp @@ -125,12 +125,6 @@ enum { LIMIT_TRIGGERED }; -//TODO remove debug -DigitalOut LED_1 = DigitalOut(P4_5) = 0; //aka D27 -DigitalOut LED_alpha = DigitalOut(P4_2) = 0; //aka D26 Alpha Endstop -DigitalOut LED_beta = DigitalOut(P7_0) = 0; //aka D25 Beta Endstop -DigitalOut LED_gamma = DigitalOut(P7_1) = 0; //aka D24 Gamma Endstop - Endstops::Endstops() { this->status = NOT_HOMING; @@ -153,7 +147,7 @@ void Endstops::on_module_loaded() // Settings this->load_config(); - THEKERNEL->slow_ticker->attach(10000, this, &Endstops::read_endstops); + THEKERNEL->slow_ticker->attach(1000, this, &Endstops::read_endstops); } // Get config @@ -406,26 +400,14 @@ void Endstops::move_to_origin(std::bitset<3> axis) // Called every millisecond in an ISR uint32_t Endstops::read_endstops(uint32_t dummy) { - LED_1 = 1; //TODO remove debug - bool endstop_triggered = false; - if(this->status != MOVING_TO_ENDSTOP_SLOW && this->status != MOVING_TO_ENDSTOP_FAST) - { - LED_1 = 0; //TODO remove debug - return 0; // not doing anything we need to monitor for - } + if(this->status != MOVING_TO_ENDSTOP_SLOW && this->status != MOVING_TO_ENDSTOP_FAST) return 0; // not doing anything we need to monitor for if(!is_corexy) { // check each axis for ( int m = X_AXIS; m <= Z_AXIS; m++ ) { if(STEPPER[m]->is_moving()) { // if it is moving then we check the associated endstop, and debounce it - //if(this->pins[m + (this->home_direction[m] ? 0 : 3)].get()) { - int n = m + (this->home_direction[m] ? 0 : 3); - endstop_triggered = this->pins[n].get(); - if(endstop_triggered) { - if (m==0) LED_alpha = 1; //TODO remove debug - if (m==1) LED_beta = 1; //TODO remove debug - if (m==2) LED_gamma = 1; //TODO remove debug + if(this->pins[m + (this->home_direction[m] ? 0 : 3)].get()) { if(debounce[m] < debounce_ms) { debounce[m]++; } else { @@ -434,9 +416,6 @@ uint32_t Endstops::read_endstops(uint32_t dummy) } } else { - if (m==0) LED_alpha = 0; //TODO remove debug - if (m==1) LED_beta = 0; //TODO remove debug - if (m==2) LED_gamma = 0; //TODO remove debug // The endstop was not hit yet debounce[m] = 0; } @@ -452,7 +431,7 @@ uint32_t Endstops::read_endstops(uint32_t dummy) if(debounce[m] < debounce_ms) { debounce[m]++; } else { - // we signal all the motors to stop, as on corexy X and Y motors will move for X and Y axis homing and we only home one axis at a time + // we signal all the motors to stop, as on corexy X and Y motors will move for X and Y axis homing and we only hom eone axis at a time STEPPER[X_AXIS]->stop_moving(); STEPPER[Y_AXIS]->stop_moving(); STEPPER[Z_AXIS]->stop_moving(); @@ -465,7 +444,7 @@ uint32_t Endstops::read_endstops(uint32_t dummy) } } } - LED_1 = 0; //TODO remove debug + return 0; } @@ -501,10 +480,6 @@ void Endstops::home(std::bitset<3> a) // reset debounce counts debounce.fill(0); - LED_alpha = 0; //TODO remove debug - LED_beta = 0; //TODO remove debug - LED_gamma = 0; //TODO remove debug - // turn off any compensation transform auto savect= THEROBOT->compensationTransform; THEROBOT->compensationTransform= nullptr; @@ -517,9 +492,7 @@ void Endstops::home(std::bitset<3> a) THEROBOT->disable_segmentation= true; // we must disable segmentation as this won't work with it enabled if(!home_z_first) home_xy(); - LED_alpha = 0; //TODO remove debug - LED_beta = 0; //TODO remove debug - LED_gamma = 0; //TODO remove debug + if(axis_to_home[Z_AXIS]) { // now home z float delta[3] {0, 0, gamma_max}; // we go the max z @@ -528,13 +501,9 @@ void Endstops::home(std::bitset<3> a) // wait for Z THECONVEYOR->wait_for_idle(); } - LED_alpha = 0; //TODO remove debug - LED_beta = 0; //TODO remove debug - LED_gamma = 0; //TODO remove debug + if(home_z_first) home_xy(); - LED_alpha = 0; //TODO remove debug - LED_beta = 0; //TODO remove debug - LED_gamma = 0; //TODO remove debug + // TODO should check that the endstops were hit and it did not stop short for some reason // we did not complete movement the full distance if we hit the endstops THEROBOT->reset_position_from_current_actuator_position(); @@ -555,9 +524,7 @@ void Endstops::home(std::bitset<3> a) THEROBOT->delta_move(delta, feed_rate, 3); // wait until finished THECONVEYOR->wait_for_idle(); - LED_alpha = 0; //TODO remove debug - LED_beta = 0; //TODO remove debug - LED_gamma = 0; //TODO remove debug + // Start moving the axes towards the endstops slowly this->status = MOVING_TO_ENDSTOP_SLOW; for ( int c = X_AXIS; c <= Z_AXIS; c++ ) { @@ -571,9 +538,7 @@ void Endstops::home(std::bitset<3> a) THEROBOT->delta_move(delta, feed_rate, 3); // wait until finished THECONVEYOR->wait_for_idle(); - LED_alpha = 0; //TODO remove debug - LED_beta = 0; //TODO remove debug - LED_gamma = 0; //TODO remove debug + // TODO should check that the endstops were hit and it did not stop short for some reason // we did not complete movement the full distance if we hit the endstops THEROBOT->reset_position_from_current_actuator_position(); @@ -870,46 +835,7 @@ void Endstops::on_gcode_received(void *argument) } break; -//TOADDBACK -/* - // NOTE this is to test accuracy of lead screws etc. - case 1910: { - // M1910.0 - move specific number of raw steps - // M1910.1 - stop any moves - // M1910.2 - move specific number of actuator units (usually mm but is degrees for a rotary delta) - if(gcode->subcode == 0 || gcode->subcode == 2) { - // Enable the motors - THEKERNEL->stepper->turn_enable_pins_on(); - int32_t x = 0, y = 0, z = 0, f = 200 * 16; - if (gcode->has_letter('F')) f = gcode->get_value('F'); - if (gcode->has_letter('X')) { - float v = gcode->get_value('X'); - if(gcode->subcode == 2) x = lroundf(v * STEPS_PER_MM(X_AXIS)); - else x = roundf(v); - STEPPER[X_AXIS]->move(x < 0, abs(x), f); - } - if (gcode->has_letter('Y')) { - float v = gcode->get_value('Y'); - if(gcode->subcode == 2) y = lroundf(v * STEPS_PER_MM(Y_AXIS)); - else y = roundf(v); - STEPPER[Y_AXIS]->move(y < 0, abs(y), f); - } - if (gcode->has_letter('Z')) { - float v = gcode->get_value('Z'); - if(gcode->subcode == 2) z = lroundf(v * STEPS_PER_MM(Z_AXIS)); - else z = roundf(v); - STEPPER[Z_AXIS]->move(z < 0, abs(z), f); - } - gcode->stream->printf("Moving X %ld Y %ld Z %ld steps at F %ld steps/sec\n", x, y, z, f); - } else if(gcode->subcode == 1) { - // stop any that are moving - for (int i = 0; i < 3; ++i) { - if(STEPPER[i]->is_moving()) STEPPER[i]->move(0, 0); - } - } - break; - } -*/ + } } } diff --git a/src/modules/tools/endstops/Endstops.h b/src/modules/tools/endstops/Endstops.h index 2089b983..387781be 100644 --- a/src/modules/tools/endstops/Endstops.h +++ b/src/modules/tools/endstops/Endstops.h @@ -5,8 +5,7 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ -#ifndef ENDSTOPS_MODULE_H -#define ENDSTOPS_MODULE_H +#pragma once #include "libs/Module.h" #include "libs/Pin.h" @@ -67,6 +66,3 @@ class Endstops : public Module{ bool move_to_origin_after_home:1; }; }; - - -#endif diff --git a/src/modules/tools/zprobe/ZProbe.cpp b/src/modules/tools/zprobe/ZProbe.cpp index 9fe64b70..9c408a16 100644 --- a/src/modules/tools/zprobe/ZProbe.cpp +++ b/src/modules/tools/zprobe/ZProbe.cpp @@ -438,8 +438,8 @@ void ZProbe::probe_XYZ(Gcode *gcode, int axis) probe_detected= false; THEROBOT->disable_segmentation= true; // we must disable segmentation as this won't work with it enabled (beware on deltas probing in X or Y) - // get probe feedrate if specified - float rate = (gcode->has_letter('F')) ? gcode->get_value('F')*60 : this->slow_feedrate; + // get probe feedrate in mm/min and convert to mm/sec if specified + float rate = (gcode->has_letter('F')) ? gcode->get_value('F')/60 : this->slow_feedrate; // do a regular move which will stop as soon as the probe is triggered, or the distance is reached switch(axis) {