Version 1.17c+2

Fixed bug in G2 and G3 commands (I and J parameters were not always
being assumed to be relative to current X and Y)
Hard fauld handler now stores the faulting PC and status registers in
the software reset data
This commit is contained in:
David Crocker 2017-01-24 15:56:11 +00:00
parent 8963082a38
commit af25fb286e
8 changed files with 100 additions and 39 deletions

View file

@ -1359,7 +1359,9 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
}
}
float initialDx = 0.0;
// The I and J parameters are always relative to present position
arcCentre[Y_AXIS] = moveBuffer.initialCoords[Y_AXIS] + jParam;
if (currentTool != nullptr)
{
// Record which axes behave like an X axis
@ -1369,30 +1371,25 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
if (axesRelative)
{
moveBuffer.coords[Y_AXIS] += yParam;
arcCentre[Y_AXIS] = moveBuffer.initialCoords[Y_AXIS] + jParam;
}
else
{
moveBuffer.coords[Y_AXIS] = yParam - currentTool->GetOffset()[Y_AXIS];
arcCentre[Y_AXIS] = jParam - currentTool->GetOffset()[Y_AXIS];
}
// Deal with the X axes
for (size_t axis = 0; axis < numAxes; ++axis)
{
arcCentre[axis] = moveBuffer.initialCoords[axis] + iParam;
if ((arcAxesMoving & (1 << axis)) != 0)
{
if (axesRelative)
{
moveBuffer.coords[axis] += xParam;
arcCentre[axis] = moveBuffer.initialCoords[axis] + iParam;
initialDx = -iParam;
}
else
{
moveBuffer.coords[axis] = xParam - currentTool->GetOffset()[axis];
arcCentre[axis] = iParam + currentTool->GetOffset()[axis];
initialDx = moveBuffer.initialCoords[axis] - arcCentre[axis];
}
}
}
@ -1400,21 +1397,16 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
else
{
arcAxesMoving = (1 << X_AXIS);
arcCentre[X_AXIS] = moveBuffer.initialCoords[X_AXIS] + iParam;
if (axesRelative)
{
moveBuffer.coords[X_AXIS] += xParam;
arcCentre[X_AXIS] = moveBuffer.initialCoords[X_AXIS] + iParam;
moveBuffer.coords[Y_AXIS] += yParam;
arcCentre[Y_AXIS] = moveBuffer.initialCoords[Y_AXIS] + jParam;;
initialDx = -iParam;
}
else
{
moveBuffer.coords[X_AXIS] = xParam;
arcCentre[X_AXIS] = iParam;
moveBuffer.coords[Y_AXIS] = yParam;
arcCentre[Y_AXIS] = jParam;
initialDx = moveBuffer.initialCoords[X_AXIS] - arcCentre[X_AXIS];
}
}
@ -1423,10 +1415,9 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
moveBuffer.xAxes = reprap.GetCurrentXAxes();
if (LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType)) // this reports an error if necessary, so no need to return true if it fails
{
const float initialDy = moveBuffer.initialCoords[Y_AXIS] - arcCentre[Y_AXIS];
arcRadius = sqrtf(initialDx * initialDx + initialDy * initialDy);
arcCurrentAngle = atan2(initialDy, initialDx);
const float finalTheta = atan2(yParam - jParam, xParam - iParam);
arcRadius = sqrtf(iParam * iParam + jParam * jParam);
arcCurrentAngle = atan2(-jParam, -iParam);
const float finalTheta = atan2(moveBuffer.coords[Y_AXIS] - arcCentre[Y_AXIS], moveBuffer.coords[X_AXIS] - arcCentre[X_AXIS]);
// Calculate the total angle moved, which depends on which way round we are going
float totalArc = (clockwise) ? arcCurrentAngle - finalTheta : finalTheta - arcCurrentAngle;

View file

@ -554,7 +554,10 @@ pre(state == provisional)
// Still going up
laDDA = laDDA->prev;
++laDepth;
if (reprap.Debug(moduleDda)) debugPrintf("Recursion start %u\n", laDepth);
if (reprap.Debug(moduleDda))
{
debugPrintf("Recursion start %u\n", laDepth);
}
}
else
{

View file

@ -84,7 +84,7 @@ void UrgentInit()
{
#ifdef DUET_NG
// When the reset button is pressed on pre-production Duet WiFi boards, if the TMC2660 drivers were previously enabled then we get
// uncommanded motor movements if the STEP lines pick up any noise. Try to reduce that by initialising the drivers early here.
// uncommanded motor movements if the STEP lines pick up any noise. Try to reduce that by initialising the pins that control the drivers early here.
// On the production boards the ENN line is pulled high and that prevents motor movements.
for (size_t drive = 0; drive < DRIVES; ++drive)
{
@ -107,6 +107,16 @@ void setup()
*heapend++ = memPattern;
}
// Trap integer divide-by-zero.
// We could also trap unaligned memory access, if we change the gcc options to not generate code that uses unaligned memory access.
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
// When doing a software reset, we disable the NRST input (User reset) to prevent the negative-going pulse that gets generated on it
// being held in the capacitor and changing the reset reason form Software to User. So enable it again here. We hope that the reset signal
// will have gone away by now.
RSTC->RSTC_MR = RSTC_MR_KEY_PASSWD | RSTC_MR_URSTEN; // ignore any signal on the NRST pin for now so that the reset reason will show as Software
// Go on and do the main initialisation
reprap.Init();
}
@ -124,18 +134,42 @@ extern "C"
return 0;
}
// Exception handlers
// By default the Usage Fault, Bus Fault and Memory Management fault handlers are not enabled,
// so they escalate to a Hard Fault and we don't need to provide separate exception handlers for them.
// We can get information on what caused a Hard Fault form the status registers.
// Also get the program counter when the exception occurred.
void prvGetRegistersFromStack(const uint32_t *pulFaultStackAddress)
{
const uint32_t pc = pulFaultStackAddress[6];
reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pc);
}
// The fault handler implementation calls a function called prvGetRegistersFromStack()
void HardFault_Handler() __attribute__((naked));
void HardFault_Handler()
{
__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 prvGetRegistersFromStack \n"
);
}
// We could set up the following fault handlers to retrieve the program counter in the same way as for a Hard Fault,
// however these exceptions are unlikely to occur, so for now we just report the exception type.
void NMI_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::NMI); }
void HardFault_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::hardFault); }
void MemManage_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::memManage); }
void BusFault_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::busFault); }
void UsageFault_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::usageFault); }
void SVC_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::otherFault); }
void DebugMon_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::otherFault); }
void PendSV_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::otherFault); }
}
// ZProbeParameters class
void ZProbeParameters::Init(float h)
@ -1181,7 +1215,7 @@ void Platform::Spin()
ClassReport(longWait);
}
void Platform::SoftwareReset(uint16_t reason)
void Platform::SoftwareReset(uint16_t reason, uint32_t pc)
{
wdt_restart(WDT); // kick the watchdog
if (reason == (uint16_t)SoftwareResetReason::erase)
@ -1240,6 +1274,9 @@ void Platform::SoftwareReset(uint16_t reason)
srdBuf[slot].magic = SoftwareResetData::magicValue;
srdBuf[slot].resetReason = reason;
GetStackUsage(NULL, NULL, &srdBuf[slot].neverUsedRam);
srdBuf[slot].hfsr = SCB->HFSR;
srdBuf[slot].cfsr = SCB->CFSR;
srdBuf[slot].pc = pc;
// Save diagnostics data to Flash
#ifdef DUET_NG
@ -1248,13 +1285,13 @@ void Platform::SoftwareReset(uint16_t reason)
DueFlashStorage::write(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
#endif
}
RSTC->RSTC_MR = RSTC_MR_KEY_PASSWD; // ignore any signal on the NRST pin for now so that the reset reason will show as Software
Reset();
for(;;) {}
}
//*****************************************************************************************************************
// Interrupts
#ifndef DUET_NG
@ -1401,10 +1438,11 @@ void Platform::Diagnostics(MessageType mtype)
}
}
Message(mtype, "Last software reset code: ");
Message(mtype, "Last software reset code ");
if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue)
{
MessageF(mtype, "0x%04x, available RAM %u bytes (slot %d)\n", srdBuf[slot].resetReason, srdBuf[slot].neverUsedRam, slot);
MessageF(mtype, "0x%04x, PC 0x%08x, HFSR 0x%08x, CFSR 0x%08x, available RAM %u bytes (slot %d)\n",
srdBuf[slot].resetReason, srdBuf[slot].pc, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].neverUsedRam, slot);
MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[srdBuf[slot].resetReason & 0x0F]);
}
else
@ -1512,6 +1550,8 @@ void Platform::Diagnostics(MessageType mtype)
void Platform::DiagnosticTest(int d)
{
static const uint32_t dummy[2] = { 0, 0 };
switch (d)
{
case (int)DiagnosticTestType::TestWatchdog:
@ -1526,6 +1566,15 @@ void Platform::DiagnosticTest(int d)
debugPrintf("Diagnostic Test\n");
break;
case (int)DiagnosticTestType::DivideByZero: // do an integer divide by zero to test exception handling
(void)RepRap::DoDivide(1, 0); // call function in another module so it can't be optimised away
break;
case (int)DiagnosticTestType::UnalignedMemoryAccess: // do an unaligned memory access to test exception handling
SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk; // by default, unaligned memory accesses are allowed, so change that
(void)RepRap::ReadDword(reinterpret_cast<const char*>(dummy) + 1); // call function in another module so it can't be optimised away
break;
case (int)DiagnosticTestType::PrintMoves:
DDA::PrintMoves();
break;

View file

@ -160,9 +160,6 @@ enum class SoftwareResetReason : uint16_t
erase = 0x10, // special M999 command to erase firmware and reset
NMI = 0x20,
hardFault = 0x30,
memManage = 0x40,
busFault = 0x50,
usageFault = 0x60,
otherFault = 0x70,
inAuxOutput = 0x0800, // this bit is or'ed in if we were in aux output at the time
stuckInSpin = 0x1000, // we got stuck in a Spin() function for too long
@ -176,6 +173,9 @@ enum class DiagnosticTestType : int
TestWatchdog = 1001, // test that we get a watchdog reset if the tick interrupt stops
TestSpinLockup = 1002, // test that we get a software reset if a Spin() function takes too long
TestSerialBlock = 1003, // test what happens when we write a blocking message via debugPrintf()
DivideByZero = 1004, // do an integer divide by zero to test exception handling
UnalignedMemoryAccess = 1005, // do an unaligned memory access to test exception handling
PrintMoves = 100, // print summary of recent moves
#ifdef DUET_NG
PrintExpanderStatus = 101, // print DueXn expander status
@ -326,7 +326,7 @@ public:
void ClassReport(float &lastTime); // Called on Spin() return to check everything's live.
void LogError(ErrorCode e) { errorCodeBits |= (uint32_t)e; }
void SoftwareReset(uint16_t reason);
void SoftwareReset(uint16_t reason, uint32_t pc = 0);
bool AtxPower() const;
void SetAtxPower(bool on);
void SetBoardType(BoardType bt);
@ -606,10 +606,10 @@ private:
// The SAM4E has a large page erase size (8K). For this reason we store the software reset data in the 512-byte user signature area
// instead, which doesn't get cleared when the Erase button is pressed. The SoftareResetData struct must have at least one 32-bit
// field to guarantee that values of this type will be 32-bit aligned. It must have no virtual members because it is read/written
// directly form/to flash memory.
// directly from/to flash memory.
struct SoftwareResetData
{
static const uint16_t versionValue = 2; // increment this whenever this struct changes
static const uint16_t versionValue = 4; // increment this whenever this struct changes
static const uint16_t magicValue = 0x7D00 | versionValue; // value we use to recognise that all the flash data has been written
static const uint32_t nvAddress = 0; // must be 4-byte aligned
static const size_t numberOfSlots = 8; // number of storage slots used to implement wear levelling
@ -617,10 +617,14 @@ private:
uint16_t magic; // the magic number, including the version
uint16_t resetReason; // this records why we did a software reset, for diagnostic purposes
uint32_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset
uint32_t hfsr; // hard fault status register
uint32_t cfsr; // configurable fault status register
uint32_t pc; // program counter when the exception occurred
bool isVacant() const // return true if this struct can be written without erasing it first
{
return magic == 0xFFFF && resetReason == 0xFFFF && neverUsedRam == 0xFFFFFFFF;
return magic == 0xFFFF && resetReason == 0xFFFF && neverUsedRam == 0xFFFFFFFF
&& hfsr == 0xFFFFFFFF && cfsr == 0xFFFFFFFF && pc == 0xFFFFFFFF;
}
};

View file

@ -1576,4 +1576,16 @@ uint32_t RepRap::GetCurrentXAxes() const
return (currentTool == nullptr) ? DefaultXAxisMapping : currentTool->GetXAxisMap();
}
// Helper function for diagnostic tests in Platform.cpp, to cause a deliberate divide-by-zero
/*static*/ uint32_t RepRap::DoDivide(uint32_t a, uint32_t b)
{
return a/b;
}
// Helper function for diagnostic tests in Platform.cpp, to cause a deliberate unaligned memory read
/*static*/ uint32_t RepRap::ReadDword(const char* p)
{
return *reinterpret_cast<const uint32_t*>(p);
}
// End

View file

@ -99,6 +99,8 @@ public:
void SetMessage(const char *msg);
static void CopyParameterText(const char* src, char *dst, size_t length);
static uint32_t DoDivide(uint32_t a, uint32_t b); // helper function for diagnostic tests
static uint32_t ReadDword(const char* p); // helper function for diagnostic tests
private:

View file

@ -9,11 +9,11 @@
#define SRC_VERSION_H_
#ifndef VERSION
# define VERSION "1.17c+1"
# define VERSION "1.17c+2"
#endif
#ifndef DATE
# define DATE "2017-01-20"
# define DATE "2017-01-24"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman"