/****************************************************************************** MODULE : TraceLeUart.c DESCRIPTION : Traceging facility via LEUART Enable in trace.h globally and/or module basis TRACE messages are written out via LEUART 20101005 AB Initial (derived from much older project) 20180808 AB Adapted to CAN-Tester, every 1s alive character to retrigger STOP_FAST_START 20191107 AB Adapted to EFM32JG1 (VC+ Ersatz), remove old references to DMA as it is all IRQ driven without DMA now, Baud rate changed from 9600 to 115200 as JADE LFRCO has to much jitter so using HFRCO ******************************************************************************/ // --- Includes --------------------------------------------------------------- #define LOGGING_TOKEN TRA_LOGGING #include "TraceLeUart.h" #include #include #include #include #include #include #include #include #include #include "types.h" #include "Hardware.h" #include "Leds.h" // --- Defines ---------------------------------------------------------------- #define BUF_MAX 7 #define WAKEUP_INTERVAL_MS 2000 #define LOG_MAX_MESSAGE_LEN 128 // max. lenght for a single TRACE message is FIXED here !!!! // even when sprintf works with longer messages, additional character // are not copied to the UART output buffer #define LOG_TX_RING_BUF_SIZE 740 // logging transmit ring buffer size #define LOG_RX_RING_BUF_SIZE 32 // logging receive ring buffer size (= max. input command len) //#define LOG_TEST_PIN_DIRECTION(value) {Nop(); TRISCbits.TRISC12=(value);} // TEST PIN //#define LOG_TEST_PIN_ON _RC12 = 1; // TEST PIN //#define LOG_TEST_PIN_OFF _RC12 = 0; // TEST PIN #define TESTPIN_INIT() { GPIO_PinModeSet(gpioPortF, 7, gpioModePushPull, 0);} #define TESTPIN( x ) { if ( x ) GPIO->P[gpioPortF].DOUT |= 1 << 7; else GPIO->P[gpioPortF].DOUT &= ~(1 << 7); } #define TRACE_NEW_LINE 0x0001 // for trace_LowLevel() #define TRACE_CARRIAGE_RETURN 0x0002 // --- Typedefs --------------------------------------------------------------- // --- PUBLIC Variables ------------------------------------------------------- #ifndef NO_TRACE //int iTraceBufLen; // last log message actual lenght char szTraceBuf[LOG_MAX_MESSAGE_LEN]; // log message buffer for user space //static char szTraceInCommand[LOG_RX_RING_BUF_SIZE]; // command input via log interface for user space #else #endif // NO_TRACE // --- Variables -------------------------------------------------------------- static volatile int iTxIrqCount ; static volatile int iRxIrqCount ; static volatile int iGpioIrqCount; #ifndef NO_TRACE static volatile char sTxRingBuf[LOG_TX_RING_BUF_SIZE]; static volatile char sRxRingBuf[LOG_RX_RING_BUF_SIZE]; static volatile int iNewCommands; static volatile int iTxBufWritePos ; static volatile int iTxBufReadPos ; static volatile int iRxBufWritePos ; static volatile int iRxBufReadPos ; static const char sAliveChars[] = "-\\|/"; // character which are logged every 1s if no user input (ALIVE) static volatile int iAliveCount; static int iCount10s; static int iSec; static int iMin; static int iHours; static int iDays; // counters for uptime write into NVM static int iSecToWriteUptime; // time between two update write (increases dynamically) static int iCountUpdateWrite; // count seconds till next update write static int iCurAliveChar; // current alive character, changed about every second static volatile int iTxRunning = FALSE; // indicates running transmission (needed for JADE) static volatile int iRxRunning = FALSE; // indicates running reception (needed for JADE) #else #endif // NO_TRACE // startup time struct { BYTE bNonValid; BYTE bSec; BYTE bMin; BYTE bHour; BYTE bDay0; BYTE bDay1; } sStartupTime; // --- Macros ----------------------------------------------------------------- #define TX_INC_WRITE_POS(); {iTxBufWritePos++; if (iTxBufWritePos > LOG_TX_RING_BUF_SIZE - 1) iTxBufWritePos = 0; } #define TX_INC_READ_POS(); {iTxBufReadPos++; if (iTxBufReadPos > LOG_TX_RING_BUF_SIZE - 1) iTxBufReadPos = 0; } #define RX_INC_WRITE_POS(); {iRxBufWritePos++; if (iRxBufWritePos > LOG_RX_RING_BUF_SIZE - 1) iRxBufWritePos = 0; } #define RX_INC_READ_POS(); {iRxBufReadPos++; if (iRxBufReadPos > LOG_RX_RING_BUF_SIZE - 1) iRxBufReadPos = 0; } // --- Functionprototypes ----------------------------------------------------- void trace_LowLevel (int iLen, int iFlags); void trace_NoLF (int iLen); // --- Code ------------------------------------------------------------------- #ifndef NO_TRACE /**************************************************************************//** * @brief Initialize Low Energy UART 1 * * Here the LEUART is initialized with the chosen settings. It is then routed * to location xx. Finally the GPIO mode is set to push pull. * *****************************************************************************/ void initLeuart(void) { /* Start LFRCO, and use LFRCO for low-energy modules */ //CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFRCO); //CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFRCO); // 20191112 does not work good enough with JADE, to much jitter // with 9600 baud. 2400 baud is okay but to slow // Enable LE (low energy) clocks CMU_ClockEnable(cmuClock_HFLE, true); // Necessary for accessing LE modules JADE CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_HFCLKLE); // Enable clocks for LEUART0 CMU_ClockEnable(cmuClock_LEUART0, true); CMU_ClockDivSet(cmuClock_LEUART0, cmuClkDiv_1); // Don't prescale LEUART clock JADE // Initialize the LEUART0 module LEUART_Init_TypeDef init = LEUART_INIT_DEFAULT; init.baudrate = 115200; LEUART_Init(LEUART0, &init); // work around for -B errate - set divider to 270 /* LF register about to be modified require sync. busy check */ //while (LEUART0->SYNCBUSY & LEUART_SYNCBUSY_CLKDIV); //LEUART_Sync(LEUART0, LEUART_SYNCBUSY_CLKDIV); //LEUART0->CLKDIV = 0x267; // work around end // Enable LEUART0 RX/TX pins on PF[4] PF[3] LEUART0->ROUTEPEN = LEUART_ROUTEPEN_RXPEN | LEUART_ROUTEPEN_TXPEN; LEUART0->ROUTELOC0 = LEUART_ROUTELOC0_RXLOC_LOC27 | LEUART_ROUTELOC0_TXLOC_LOC27; /* Enable GPIO for LEUART0. TX is on PF3 */ GPIO_PinModeSet(gpioPortF, /* GPIO port */ 3, /* GPIO port number */ gpioModePushPull, /* Pin mode is set to push pull */ 1); /* High idle state */ /* Enable GPIO for LEUART0. RX is on PF4 */ GPIO_PinModeSet(gpioPortF, /* Port */ 4, /* Port number */ gpioModeInputPull, /* Pin mode is set to input only, with pull direction given bellow */ 1); /* Pull direction is set to pull-up */ // and generate IRQ on falling edge in addition to RX function // this is to wake up from EM2 on falling edge so LEUART can take over reception afterwards // Configure PF4 interrupt on falling edge GPIO_IntConfig(gpioPortF, 4, false, true, true); /* Enable GPIO_EVEN interrupt vector in NVIC and priority */ NVIC_SetPriority(GPIO_EVEN_IRQn, 7); // same priority as UART IRQ below, don't set different priority! NVIC_EnableIRQ(GPIO_EVEN_IRQn); // enable receive IRQ and TX buffer low and (JADE, HFCLK) TX completed LEUART_IntEnable(LEUART0, LEUART_IEN_RXDATAV | LEUART_IEN_TXBL | LEUART_IF_TXC); /* Enable LEUART0 interrupt vector and set priority */ NVIC_SetPriority(LEUART0_IRQn, 7); // same priority as GPIO IRQ above, don't set different priority! NVIC_EnableIRQ(LEUART0_IRQn); } /****************************************************************************** void GPIO_EVEN_IRQHandler(void) Interrupt handler GPIO even numbered pins ******************************************************************************/ void GPIO_EVEN_IRQHandler(void) { LedOn(2); iRxRunning = TRUE; iGpioIrqCount++; /* clear flag for PF4 RX interrupt */ if ( GPIO_IntGet() & (1 << 4) ) { GPIO_IntClear( (1 << 4) ); } NVIC_ClearPendingIRQ(GPIO_EVEN_IRQn); } /**************************************************************************//** * @brief LEUART IRQ handler * * Called either when TX fifo is empty or RX buffer has data and transmission * is over (JADE) * ******************************************************************************/ void LEUART0_IRQHandler(void) { char c; LEUART_TypeDef *leuart; LedOn(2); leuart = LEUART0; if ( LEUART_IntGet(leuart) & LEUART_IF_TXC ) { // JADE: trasmission completed, save to go into EM2 iTxRunning = FALSE; TESTPIN(0); LEUART_IntClear(leuart, LEUART_IF_TXC); } if ( LEUART_IntGet(leuart) & LEUART_IF_TXBL ) { // trasmitter buffer empty iTxIrqCount++; while ( (iTxBufReadPos != iTxBufWritePos) && (LEUART_StatusGet(leuart) & LEUART_STATUS_TXBL) ) // as long as char are in ring buffer and transmitter ready { iTxRunning = TRUE; TESTPIN(1); LEUART_Tx(leuart, sTxRingBuf[iTxBufReadPos]); TX_INC_READ_POS(); } if ( iTxBufReadPos == iTxBufWritePos ) { // transmission completed LEUART_IntDisable(leuart, LEUART_IF_TXBL); } LEUART_IntClear(leuart, LEUART_IF_TXBL); } if ( LEUART_IntGet(LEUART0) & LEUART_IF_RXDATAV ) { // Read the receive buffer while a character is available iRxIrqCount++; while ( leuart->STATUS & LEUART_STATUS_RXDATAV ) { c = (char)(leuart->RXDATAX); if ( c == '\r' || c == '\n' ) { iNewCommands++; } sRxRingBuf[iRxBufWritePos] = c; RX_INC_WRITE_POS(); } iAliveCount = -9; // disable alive output for another 10s when characters are received iRxRunning = FALSE; LEUART_IntClear(leuart, LEUART_IF_RXDATAV); } // clear all LEUART IRQs //LEUART_IntClear(leuart, _LEUART_IFC_RESETVALUE); NVIC_ClearPendingIRQ(LEUART0_IRQn); } /****************************************************************************** char * TraceGetBuildTime (void) Returns pointer to string which holds build time ******************************************************************************/ char * TraceGetBuildTime(void) { return __TIME__; } /****************************************************************************** char * TraceGetBuildDate (void) Returns pointer to string which holds build time ******************************************************************************/ char * TraceGetBuildDate(void) { return __DATE__; } /****************************************************************************** void TraceInit (void) Initializes UART and logging ring buffer counters and sends startup message. ******************************************************************************/ void TraceInit (void) { TESTPIN_INIT(); // clear/set variables iAliveCount = 1; iCount10s = 1; iSec = 0; iMin = 0; iHours = 0; iDays = 0; // clear buffer position pointers iTxBufWritePos = 0; iTxBufReadPos = 0; iRxBufWritePos = 0; iRxBufReadPos = 0; iTxIrqCount = 0; iRxIrqCount = 0; iGpioIrqCount = 0; iCurAliveChar = 0; CMU_ClockEnable(cmuClock_GPIO, true); /* Enable GPIO clock */ /* Initialize LEUART */ initLeuart(); TRACE ("\n"); // 2 \n cause the pin maybe not in idle state so receiver would possibly ignore first char // and at least one new line is desired TRACE ("*************************************************"); TracePrintFirmwareVersion(); // ROWLEY Crossworks - __DEBUG__ is set in Project - Properties - DEBUG - PreProcessor... for debug builds // MPLAB - __DEBUG will be set with debug builds #if defined (__DEBUG__) || defined (DEBUG) || defined (__DEBUG) TRACE (" DEBUG"); #endif TracePrintHwInfo(); TRACE ("*************************************************"); // init counters for uptime write into NVM iSecToWriteUptime = 4; iCountUpdateWrite = 4; } /****************************************************************************** void TraceInit2 (void) Initialization second part (uptime, startup time) ******************************************************************************/ void TraceInit2 (void) { // get uptime from EEPROM TRACE("TRA: init2"); /* if (NvmGiveBootStatus() != NVM_INIT_CHECK_VIRGIN)// device started with valid NVM config { TraceReadUptimeFromNvm(); } else // NVM config not valid, clear time, { // variables are initialized to 0, so TraceWriteUptimeToNvm(); // Uptime can safely be written } TraceWriteStartupTimeToNvm(); TracePrintStartTime(); */ } /****************************************************************************** void TraceTicker1s(void) Called every 1s (app.). Simple SW clock and alive signalling. ******************************************************************************/ void TraceTicker1s(void) { if ( TRA_LOGGING > 2 ) { TracePrintIRQs(); } iSec++; if ( iSec > 59 ) { iSec = 0; iMin++; if ( iMin > 59 ) { iMin = 0; iHours++; if ( iHours > 23 ) { iHours = 0; iDays++; } } } iCount10s++; if ( iCount10s > 10 ) { iCount10s = 1; } // alive signaling - if no logging input/output for more than 1s, send alive character (to retrigger STOP_FAST_START) iAliveCount++; if ( iAliveCount > 1 ) // every 1 seconds, but only when no character input on logging interface !!!!!! { iAliveCount = 1; if ( TRA_LOGGING > 1 ) { TracePrintUptime(); } else { TRACE_NOLF("%c", sAliveChars[iCurAliveChar]); // alive iCurAliveChar++; if ( iCurAliveChar >= (int) strlen(sAliveChars) ) { iCurAliveChar = 0; } TRACE_NOLF("%c", 0x08); // backspace } } iCountUpdateWrite--; // check if uptime should be written if ( !iCountUpdateWrite ) { TRACE_L2("TRA: write uptime to NVM"); TraceWriteUptimeToNvm(); if ( iSecToWriteUptime < 50000 ) // time between uptime write is dynamically increased till 50000sec (about 14h) // in steps of 1/16 of current time step. So at max. 1/16 time since bLogLevel will // be lost in case of power loss, but never more than 14h { iSecToWriteUptime += iSecToWriteUptime / 16 + 8; TRACE_L2("TRA: next uptime write in %us", iSecToWriteUptime); } iCountUpdateWrite = iSecToWriteUptime; } } /****************************************************************************** void trace (int) Forwarder to trace_LowLevel with new line and carriage return ******************************************************************************/ void trace (int iLen) { trace_LowLevel(iLen, TRACE_NEW_LINE | TRACE_CARRIAGE_RETURN); } /****************************************************************************** void trace_NoLF (int) Forwarder to trace_LowLevel without new line and carriage return ******************************************************************************/ void trace_NoLF (int iLen) { trace_LowLevel(iLen, 0); } /****************************************************************************** void trace_LowLevel (int) Used with macro TRACE. Copies new output string to transmit buffer and appends \n\r !!!! CHARACTERS ARE LOST/OVERWRITTEN WHEN TOO MUCH NEW CHARs are filled in iLen: Lenght of string (excluding \n\r) as delivered by sprintf in macro definition, Message is in global var szTraceBuf[] iFlags: TRACE_NEW_LINE or TRACE_CARRIAGE_RETURN are honored ******************************************************************************/ void trace_LowLevel (int iLen, int iFlags) { WORD i; BYTE bOverflow = FALSE; LEUART_IntDisable(LEUART0, LEUART_IF_TXBL); // disable TX IRQ while pointer increment for ( i = 0; i < iLen; i++ ) { ITM_SendChar(szTraceBuf[i]); // put it out at SWO too sTxRingBuf[iTxBufWritePos] = szTraceBuf[i]; // put char from TraceBuf to output ring buffer TX_INC_WRITE_POS(); if ( iTxBufWritePos == iTxBufReadPos ) bOverflow = TRUE; } // append \r\n to trace messages if desired if ( iFlags & TRACE_NEW_LINE ) { ITM_SendChar('\n'); // put it out at SWO too sTxRingBuf[iTxBufWritePos] = '\n'; // put \n in ring buffer TX_INC_WRITE_POS(); if ( iTxBufWritePos == iTxBufReadPos ) bOverflow = TRUE; } if ( iFlags & TRACE_CARRIAGE_RETURN ) { ITM_SendChar('\r'); // put it out at SWO too sTxRingBuf[iTxBufWritePos] = '\r'; // put \r in ring buffer TX_INC_WRITE_POS(); if ( iTxBufWritePos == iTxBufReadPos ) bOverflow = TRUE; } if ( bOverflow ) // mark overflow in output stream with *** { sTxRingBuf[iTxBufWritePos] = '\n'; // put \n in ring buffer TX_INC_WRITE_POS(); sTxRingBuf[iTxBufWritePos] = '\r'; // put \n in ring buffer TX_INC_WRITE_POS(); sTxRingBuf[iTxBufWritePos] = '*'; // put \r in ring buffer TX_INC_WRITE_POS(); sTxRingBuf[iTxBufWritePos] = '*'; // put \r in ring buffer TX_INC_WRITE_POS(); sTxRingBuf[iTxBufWritePos] = '*'; // put \r in ring buffer TX_INC_WRITE_POS(); sTxRingBuf[iTxBufWritePos] = '\n'; // put \r in ring buffer TX_INC_WRITE_POS(); sTxRingBuf[iTxBufWritePos] = '\r'; // put \r in ring buffer TX_INC_WRITE_POS(); } iTxRunning = TRUE; LEUART_IntSet(LEUART0, LEUART_IF_TXBL); // activate TX IRQ LEUART_IntEnable(LEUART0, LEUART_IF_TXBL);; // reenable TX IRQ iAliveCount = 1; // disable alive character for another 1s //10s } /****************************************************************************** char TraceGetKey (void) Returns next byte in logging input stream ******************************************************************************/ char TraceGetKey(void) { char c; LEUART_IntDisable(LEUART0, LEUART_IF_RXDATAV); // disable RX IRQ while pointer increment if ( iRxBufReadPos != iRxBufWritePos ) // character available ? { c = sRxRingBuf[iRxBufReadPos]; RX_INC_READ_POS(); } else c = 0; // indicate read error LEUART_IntEnable(LEUART0, LEUART_IF_RXDATAV); // reenable RX IRQ return c; } /****************************************************************************** void TracePrintUptime (void) Sends uptime string to logging interface ******************************************************************************/ void TracePrintUptime(void) { TRACE("TRA: uptime %5d days %02d:%02d:%02d", iDays, iHours, iMin, iSec); } /****************************************************************************** void TracePrintStartTime (void) Sends start time string to logging interface ******************************************************************************/ void TracePrintStartTime(void) { WORD wD = 0; BYTE bS = 0, bM = 0, bH = 0; BYTE *pbPos; pbPos = &sStartupTime.bNonValid; // write time bS = *(pbPos + 1); bM = *(pbPos + 2); bH = *(pbPos + 3); wD = *(pbPos + 4); wD += *(pbPos + 5) * 256; TRACE("TRA: boot at%5d days %02d:%02d:%02d", wD, bH, bM, bS); } /****************************************************************************** void TracePrintIRQs (void) Prints out number of uart IRQs at logging interface ******************************************************************************/ void TracePrintIRQs(void) { LEUART_IntDisable(LEUART0, LEUART_IF_TXBL | LEUART_IF_RXDATAV); TRACE("TRA: IRQs TX: %u, RX: %u, GPIO: %u", iTxIrqCount, iRxIrqCount, iGpioIrqCount); LEUART_IntEnable(LEUART0, LEUART_IF_TXBL | LEUART_IF_RXDATAV); } /****************************************************************************** void TraceReadUptimeFromNvm (void) Reads uptime info from NVM to RAM. Uptime is stored in 2 arrays. A NONVALID flag indicates the valid array. If both are valid, last write was interrupted and set1 is used. ******************************************************************************/ void TraceReadUptimeFromNvm(void) { /* BYTE bPos = 0; // check which position is VALID if ( NvmGetChar(NVM_UPTIME1_NONVALID) == FALSE ) { bPos = NVM_UPTIME1_NONVALID; } else { if ( NvmGetChar(NVM_UPTIME2_NONVALID) == FALSE ) { bPos = NVM_UPTIME2_NONVALID; } } // read time from valid position, if both are invalid, clear time if ( bPos ) // at least on array is valid { iSec = NvmGetChar(bPos + 1); iMin = NvmGetChar(bPos + 2); iHours = NvmGetChar(bPos + 3); iDays = NvmGetChar(bPos + 4); iDays += NvmGetChar(bPos + 5) * 256; TracePrintUptime(); } else // no valid array found, clear time { iSec = 0; iMin = 0; iHours = 0; iDays = 0; TraceWriteUptimeToNvm(); // write valid uptime to NVM TRACE("TRA: no valid uptime found in NVM"); } */ } /****************************************************************************** void TraceWriteUptimeToNvm (void) Stores uptime info to NVM. Uptime is stored in 2 arrays. A NONVALID flag is cleard after sucessful write. NOVALID flag of other set is set. So usually only one set is valid. If both are valid, last write was interrupted. ******************************************************************************/ void TraceWriteUptimeToNvm(void) { /* BYTE bPosNew, bPosOld; // check which position is NONVALID if ( NvmGetChar(NVM_UPTIME1_NONVALID) ) { bPosNew = NVM_UPTIME1_NONVALID; bPosOld = NVM_UPTIME2_NONVALID; } else { bPosNew = NVM_UPTIME2_NONVALID; bPosOld = NVM_UPTIME1_NONVALID; } // write time to currently NONVALID position NvmStoreChar(bPosNew + 1, iSec); NvmStoreChar(bPosNew + 2, iMin); NvmStoreChar(bPosNew + 3, iHours); NvmStoreChar(bPosNew + 4, iDays & 0xFF); NvmStoreChar(bPosNew + 5, iDays >> 8); // set written array to valid, and older array to NONVALID NvmStoreChar(bPosNew, FALSE); NvmStoreChar(bPosOld, 0x55); // for better readabiliy in EEProm list TRACE_L2("TRA: uptime written to NVM (pos: 0x%02X)", bPosNew); */ TRACE_L2("TRA: No NVM - no uptime"); } /****************************************************************************** void TraceWriteStartupTimeToNvm (void) Stores startup time info to NVM. NONVALID flag is not used here! ******************************************************************************/ void TraceWriteStartupTimeToNvm (void) { BYTE *pbPos; pbPos = &sStartupTime.bNonValid; // write time *(pbPos + 1) = iSec; *(pbPos + 2) = iMin; *(pbPos + 3) = iHours; *(pbPos + 4) = iDays & 0xFF; *(pbPos + 5) = iDays >> 8; //TRACE_L2("TRA: startup time written to NVM (pos: 0x%02X)", bPos); } /****************************************************************************** void TracePrintHwInfo(void); Prints hardware info ******************************************************************************/ void TracePrintHwInfo(void) { // TRACE (" %s: %d, DIP-Switch: 0x%02X", HwGetType() == 0 ? "Master" : "Slave ", HwGetType(), HwGetType()); //, HwGetDescription() ); // TRACE (" HW-Ver: %2d Rev: %2d, MAC-Address: 0x%04X", HwGetVersion(), HwGetRevision(), HwGetMacAddr() ); TRACE (" HW-Ver: %2d Rev: %2d", HwGetVersion(), HwGetRevision() ); } /****************************************************************************** void TracePrintFirmwareVersion(void); Prints info about firmware software version ******************************************************************************/ void TracePrintFirmwareVersion() { TRACE (" JADE ADC-Test Build: "__DATE__" "__TIME__"" ); return; } /****************************************************************************** int TraCommandAvail(void) Returns number of available commands in input buffer ToDo: - interface to command.c should better be changed to TraGetCommand so no TraceCommandAvailxxx would be needed anymore ******************************************************************************/ int TraceCommandAvail() { if ( iNewCommands < 0 ) iNewCommands = 0; return iNewCommands; } /****************************************************************************** void TraceCommandAvailDec (void) Decreases number of available commands in input buffer ToDo: - interface to command.c should better be changed to TraGetCommand so no TraceCommandAvailxxx would be needed anymore ******************************************************************************/ void TraceCommandAvailDec(void) { if ( iNewCommands > 0 ) iNewCommands--; } /****************************************************************************** int TraceTxIsRunning(void) Returns TRUE when data trasminssion is not finished until last bit is shifted out. ******************************************************************************/ __INLINE int TraceIsRunning(void) { return iTxRunning | iRxRunning; } /****************************************************************************** __asm void hard_fault_handler_asm(void) Cortex-M3 fault handler Hard fault handler wrapper in assembly Extracts the location of stack frame and pass it to handler in C as pointer. ******************************************************************************/ void HardFault_Handler(void) __attribute__( ( naked ) ); void HardFault_Handler(void) { __ASM("TST LR, #4"); __ASM("ITE EQ"); __ASM("MRSEQ R0, MSP"); __ASM("MRSNE R0, PSP"); __ASM("B hard_fault_handler_c"); } /****************************************************************************** void hard_fault_handler_c(unsigned int * hardfault_args) Cortex-M3 fault handler The second part of the handler is in C. Here, we demonstrate how the stacked register contents and the Fault Status registers can be accessed. with stack frame location as input parameter The logging buffer is flushed with direct calls to UART IRQ service routine. As the systems crashed hard anyway, any dirty hacks can be done here. System restarts afterwards. ******************************************************************************/ void hard_fault_handler_c(unsigned int * hardfault_args) { unsigned int stacked_r0; unsigned int stacked_r1; unsigned int stacked_r2; unsigned int stacked_r3; unsigned int stacked_r12; unsigned int stacked_lr; unsigned int stacked_pc; unsigned int stacked_psr; 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]); TRACE ("\n#####################################"); TRACE ("[Cortex-M3 Hard fault handler]"); TRACE (" R0 = 0x%08X", stacked_r0); TRACE (" R1 = 0x%08X", stacked_r1); TRACE (" R2 = 0x%08X", stacked_r2); TRACE (" R3 = 0x%08X", stacked_r3); TRACE (" R12= 0x%08X", stacked_r12); TRACE (" LR = 0x%08X - LinkRegister", stacked_lr); TRACE (" PC = 0x%08X", stacked_pc); TRACE ("\n"); TRACE (" PSR = 0x%08X - ProgramStatus", stacked_psr); TRACE (" BFAR= 0x%08X - BusFaultAddress", (*((volatile unsigned long *)(0xE000ED38)))); TRACE (" CFSR= 0x%08X - UsageFault1|UsageFault0|BusFault|MemManFault", (*((volatile unsigned long *)(0xE000ED28)))); TRACE (" HFSR= 0x%08X - HardFaultStatus", (*((volatile unsigned long *)(0xE000ED2C)))); TRACE (" DFSR= 0x%08X - DebugFaultStatus", (*((volatile unsigned long *)(0xE000ED30)))); TRACE (" AFSR= 0x%08X - AuxFaultStatus", (*((volatile unsigned long *)(0xE000ED3C)))); TRACE ("#####################################\n"); while ( (iTxBufReadPos != iTxBufWritePos) ) { LEUART0_IRQHandler(); // flush TX buffer to logging console } // restart system NVIC_SystemReset(); return; } //****************************************************************************** //****************************************************************************** // empty function definitions in case NO_TRACE is selected #else void trace (int i) { return; }; void TraceInit (void) { return; }; void TraceInit2 (void) { return; }; void TraceTicker1s() { return; }; char TraceGetKey(void) { return 0; }; void TracePrintUptime(void) { return; }; void TracePrintStartTime(void) { return; }; void TracePrintIRQs(void) { return; }; void TracePrintHwInfo(void) { return; }; void TracePrintKeypadInfo(void) { return; }; void TracePrintFirmwareVersion(void) { return; }; #endif // NO_TRACE