Expanding the Error LED functionality

Information on the latest releases
Post Reply
peekay123
Posts: 18
Joined: Tue Nov 08, 2016 8:39 pm

Expanding the Error LED functionality

Post by peekay123 » Wed Aug 02, 2017 7:37 pm

@RogerClark, I've had great success working on a custom board based on an STM32F103RCT6 using the STM32Duino code (THANKS!). In my configuration I've set an Error LED (PA15) and it works very well since yesterday, on a critical fault, it went into "throbbing" mode.

Looking into the code, I noticed that exc.s stuffs a unique number, which represents the fault, into register "r0" and places it as the first value on the stack when it jumps to __error() which does its thing and calls throb. Being a long time user of Particle Photons, I've come to appreciate their "SOS" blinking pattern used to visualize such faults. For example, a flashing S-O-S pattern followed by one blink is a heap fault.

It would be great to extend the throb() functionality to do this, however, I'm not sure how to fetch the "r0" value from the stack into a variable in order drive the throb flashing pattern accordingly. Any guidance would be appreciated.

:D

User avatar
RogerClark
Posts: 6893
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Expanding the Error LED functionality

Post by RogerClark » Wed Aug 02, 2017 10:13 pm

There were some examples of calling assembler from C which were posted to the forum, probably 6 months ago.

I think they included an example of a function where C variables were passed to asssembler which did a basic calculation e.g. and addition, and returned the result to the C variable.

You should be able to modify that C code to access register R0.

BTW. you mention R0 on the stack ? But R0 is a register e.g. Like a variable, and nothing to do with the stack.

So I am. It sure if you need to pull a value from the stack or read R0

PS. If you want to flash a number via the LED, morse code is the obvious way. It's not hard to learn the numbers , as the are all 5 flashes long, and use a logical sequence of flashes

peekay123
Posts: 18
Joined: Tue Nov 08, 2016 8:39 pm

Re: Expanding the Error LED functionality

Post by peekay123 » Wed Aug 02, 2017 11:25 pm

@RogerClark, exc.s massages the stack, leaving the value in r0 as the top of the stack prior to jumping to __error(). Now I need to pop it from the stack and store it in a variable I can work with. I'll fish around for a way to do that.

peekay123
Posts: 18
Joined: Tue Nov 08, 2016 8:39 pm

Re: Expanding the Error LED functionality

Post by peekay123 » Thu Aug 03, 2017 2:43 am

Roger, I have it all working now along with a compile flag in util.h to switch the morse feature ON or OFF (throbbing LED). Not sure I should PR or simply post the code sections here. Any thoughts?

User avatar
RogerClark
Posts: 6893
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Expanding the Error LED functionality

Post by RogerClark » Thu Aug 03, 2017 3:05 am

You could post here first, as far more people read the forums than look on github

peekay123
Posts: 18
Joined: Tue Nov 08, 2016 8:39 pm

Re: Expanding the Error LED functionality

Post by peekay123 » Thu Aug 03, 2017 12:58 pm

I recently ported a I2C GPS library for use on a custome STM32F103 board using STM32Duino. The code seemed to work well for a few moments until I noticed nothing was being output to the Serial console and the onboard LED that I had designated as the "error" LED what pulsing indicating an fault/exception. The pulsing didn't convey WHAT the fault was, only that a fault had occurred. The STM32F1xx handles 5 exception conditions:

nmi 1
hardfault 2
memmanage 3
busfault 4
usagefault 5

Having worked on other STM32 platforms before (Particle), I looked into the core code to see if I could modify the pulsing code to flash according to the exception number. Looking at exc.S, i found that code set the corresponding exception number in register r0 and then jumped to __error() in util.c. This function in turn called throb() to create a "breathing" effect on the designated ERROR LED. With a bit assembler to fetch the value in r0 into a 'c' variable, I was able to modify __error() to pass that value to throb() which then flashed the error code using short pulses with longer pause between sequences. So a MEMMANAGE exception would quickly flash the LED 3 times, pause and repeat the pattern. This modification now saved me the hassle of connecting my ST-LINK v2, getting the debugger up and setting a breakpoint to then examine the register value in exc.S Hopefully, it will be useful to others.

The modified util.c and util.h files, found in \Documents\Arduino\hardware\Arduino_STM32\STM32F1\system\libmaple\include\libmaple follow. There is a compile flag in util.h called "ERROR_MORSE" to enable the feature. If not enabled, the regular throbbing LED will be used if an ERROR LED is defined.

util.c

Code: Select all

/******************************************************************************
 * The MIT License
 *
 * Copyright (c) 2010 Perry Hung.
 * Copyright (c) 2011, 2012 LeafLabs, LLC.
 *
 * 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.
 *****************************************************************************/

/**
 * @file libmaple/util.c
 * @brief Utility procedures for debugging
 */

#include <libmaple/libmaple.h>
#include <libmaple/usart.h>
#include <libmaple/gpio.h>
#include <libmaple/nvic.h>

/* (Undocumented) hooks used by Wirish to direct our behavior here */
extern __weak void __lm_error(void);
extern __weak usart_dev* __lm_enable_error_usart(void);

/* If you define ERROR_LED_PORT and ERROR_LED_PIN, then a failed
 * ASSERT() will also throb() an LED connected to that port and pin.
 */
#if defined(ERROR_LED_PORT) && defined(ERROR_LED_PIN)
#define HAVE_ERROR_LED
#endif

/* (Called from exc.S with global interrupts disabled.) */
__attribute__((noreturn)) void __error(void) {
#if defined(ERROR_MORSE)
	uint32 excptnum;
	__asm volatile ( " mov %0, #3  \n" :"=&r"(excptnum)	);
#endif

    if (__lm_error) {
        __lm_error();
    }
    /* Reenable global interrupts */
    nvic_globalirq_enable();
#if defined(ERROR_MORSE)
    throb(excptnum);
#else
	throb();
#endif
}

/*
 * Print an error message on a UART upon a failed assertion (if one is
 * available), and punt to __error().
 *
 * @param file Source file of failed assertion
 * @param line Source line of failed assertion
 * @param exp String representation of failed assertion
 * @sideeffect Turns of all peripheral interrupts except USB.
 */
void _fail(const char* file, int line, const char* exp) {
    if (__lm_enable_error_usart) {
        /* Initialize the error USART */
        usart_dev *err_usart = __lm_enable_error_usart();

        /* Print failed assert message */
        usart_putstr(err_usart, "ERROR: FAILED ASSERT(");
        usart_putstr(err_usart, exp);
        usart_putstr(err_usart, "): ");
        usart_putstr(err_usart, file);
        usart_putstr(err_usart, ": ");
        usart_putudec(err_usart, line);
        usart_putc(err_usart, '\n');
        usart_putc(err_usart, '\r');
    }
    /* Shutdown and error fade */
    __error();
}

/*
 * Provide an __assert_func handler to libc so that calls to assert()
 * get redirected to _fail.
 */
void __assert_func(const char* file, int line, const char* method,
                   const char* expression) {
    _fail(file, line, expression);
}

/*
 * Provide an abort() implementation that aborts execution and punts
 * to __error().
 */
void abort() {
    if (__lm_enable_error_usart) {
        /* Initialize the error USART */
        usart_dev *err_usart = __lm_enable_error_usart();
        /* Print abort message. */
        usart_putstr(err_usart, "ERROR: PROGRAM ABORTED VIA abort()\r\n");
    }

    /* Shutdown and error fade */
    __error();
}

/* This was public as of v0.0.12, so we've got to keep it public. */
/**
 * @brief Fades the error LED on and off
 * @sideeffect Sets output push-pull on ERROR_LED_PIN.
 */
#if defined(ERROR_MORSE)
__attribute__((noreturn)) void throb(uint32 excptn) {
#else
__attribute__((noreturn)) void throb(void) {
#endif

#ifdef HAVE_ERROR_LED
    int32  slope   = 1;
    uint32 CC      = 0x0000;
    uint32 TOP_CNT = 0x0200;
    uint32 i       = 0;

 #if defined(ERROR_MORSE)
    gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_MODE_OUTPUT);

	while(1) {
		for (i = 0; i < excptn; i++) {
			gpio_write_bit(ERROR_LED_PORT, ERROR_LED_PIN, 0);
			delay_us(300000); 
			gpio_write_bit(ERROR_LED_PORT, ERROR_LED_PIN, 1);
			delay_us(300000); 
		}
		delay_us(2000000); 
	}
 #else
    gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_MODE_OUTPUT);
	
    /* Error fade. */
    while (1) {
        if (CC == TOP_CNT)  {
            slope = -1;
        } else if (CC == 0) {
            slope = 1;
        }

        if (i == TOP_CNT)  {
            CC += slope;
            i = 0;
        }

        if (i < CC) {
            gpio_write_bit(ERROR_LED_PORT, ERROR_LED_PIN, 1);
        } else {
            gpio_write_bit(ERROR_LED_PORT, ERROR_LED_PIN, 0);
        }
        i++;
    }
 #endif // ERROR_MORSE
#else
    /* No error LED is defined; do nothing. */
    while (1)
        ;
#endif
}
util.h

Code: Select all

/******************************************************************************
 * The MIT License
 *
 * Copyright (c) 2010 Perry Hung.
 *
 * 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.
 *****************************************************************************/

/**
 * @file libmaple/include/libmaple/util.h
 * @brief Miscellaneous utility macros and procedures.
 */

#ifndef _LIBMAPLE_UTIL_H_
#define _LIBMAPLE_UTIL_H_

#ifdef __cplusplus
extern "C"{
#endif

#include <libmaple/libmaple_types.h>

/*
 * Bit manipulation
 */

/** 1UL shifted left by 'shift' */
#define BIT(shift)                     (1UL << (shift))
/** 'Mask' shifted left by 'shift' */
#define BIT_MASK_SHIFT(mask, shift)    ((mask) << (shift))
/** Bits m to n of x */
#define GET_BITS(x, m, n) ((((uint32)x) << (31 - (n))) >> ((31 - (n)) + (m)))
/** True iff v is a power of two (1, 2, 4, 8, ...) */
#define IS_POWER_OF_TWO(v)  ((v) && !((v) & ((v) - 1)))

/*
 * Failure routines
 */

/*	Use LED morse to indicate fatal execption code (see exc.S)
	If commented out, default "throbbing" LED will be used
	Exception code LED pulses:
		nmi			1
		hardfault	2
		memmanage	3
		busfault	4
		usagefault	5
*/
#define ERROR_MORSE		// Comment out to use throbbing LED

void __error(void);
void _fail(const char*, int, const char*);
#if defined(ERROR_MORSE)
 void throb(uint32);
#else
 void throb(void);
#endif

/*
 * Asserts and debug levels
 */

#define DEBUG_NONE      0
#define DEBUG_FAULT     1
#define DEBUG_ALL       2

/**
 * \def DEBUG_LEVEL
 *
 * Controls the level of assertion checking.
 *
 * The higher the debug level, the more assertions will be compiled
 * in.  This increases the amount of debugging information, but slows
 * down (and increases the size of) the binary.
 *
 * The debug levels, from lowest to highest, are DEBUG_NONE,
 * DEBUG_FAULT, and DEBUG_ALL.  The default level is DEBUG_ALL.
 */

#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL DEBUG_ALL
#endif

#if DEBUG_LEVEL >= DEBUG_ALL
#define ASSERT(exp)                              \
    if (exp) {                                   \
    } else {                                     \
        _fail(__FILE__, __LINE__, #exp);         \
    }
#else
#define ASSERT(exp) (void)((0))
#endif

#if DEBUG_LEVEL >= DEBUG_FAULT
#define ASSERT_FAULT(exp)                       \
    if (exp) {                                  \
    } else {                                    \
        _fail(__FILE__, __LINE__, #exp);        \
    }
#else
#define ASSERT_FAULT(exp) (void)((0))
#endif

#ifdef __cplusplus
} // extern "C"
#endif

#endif
:D

User avatar
Rick Kimball
Posts: 1011
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: Expanding the Error LED functionality

Post by Rick Kimball » Thu Aug 03, 2017 1:05 pm

Neat trick! ... but in the end don't you have to connect up the debugger to figure out why you got one of those exceptions?
-rick

peekay123
Posts: 18
Joined: Tue Nov 08, 2016 8:39 pm

Re: Expanding the Error LED functionality

Post by peekay123 » Thu Aug 03, 2017 3:47 pm

Rick, maybe not! Knowing you have a memory exception might be obvious to spot in your code. Just saying ;)

User avatar
RogerClark
Posts: 6893
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Expanding the Error LED functionality

Post by RogerClark » Thu Aug 03, 2017 10:14 pm

Thanks

I will need to incorporate this

peekay123
Posts: 18
Joined: Tue Nov 08, 2016 8:39 pm

Re: Expanding the Error LED functionality

Post by peekay123 » Fri Aug 04, 2017 12:02 pm

Roger, let me know if you need me to do anything.

:D

Post Reply