How do I debug unexpected resets in a STM32 device?
Asked Answered
M

6

13

I'm doing some development in C with a STM32F107 chip and, at some point, the device began to reset when I call a specific function. I don't have a debugger and my debugging is just plain text over a serial port.

I've used some other microcontrollers in which I was able to access a register to see the cause of the reset, but I can't seem to find an equivalent for this device. I'm aware of the hardware exceptions of the Cortex-M3, but I don't know if one of them is being triggered since I can't seem to send text over usart when I'm inside those handlers (maybe because my TX functions use interruptions?).

So, I decided to ask people with more experience than I in this device: what is usually done to debug situations like these?

EDIT

One of the developers activated the WWDG watchdog and it was reseting the hardware before I could get my info from the fault handlers. It was a Hard Fault due to calling a function by a pointer that was pointing to the wrong place. However, I will keep this question in the hope that someone will give more details (or material about it) for pointing back to C code from the registers saved in, lets say, a Hard Fault (@dwelch idea).

Macmillan answered 10/1, 2012 at 16:7 Comment(0)
H
12

The Cortex M3 has excellent fault handling features that will make your life easier. On hitting a fault, it automatically stacks several registers like PC and LR, and fault status registers will tell you things like address of bus fault, etc.

You should implement a good fault handler (for example, the hard fault handler here: http://blog.frankvh.com/2011/12/07/cortex-m3-m4-hard-fault-handler/) to print out the stacked registers and debug fault status registers.

You should use the UART for printing, just write your own simple custom version of printf for use from your fault handler that doesn't depend on interrupts. Just write bytes directly to uart Tx data register and poll for byte completion.

Haerle answered 10/1, 2012 at 17:57 Comment(0)
R
3

Aside from what has been mentioned about the interrupt handlers for debugging, some ST micros also have a reset source register that you can read on power-up (that is after a reset). For the cortex M family (m0/m3/m4) the register is RCC_CSR. http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf

Unfortunately you wouldn't be able to know whether the specifics, such as hard fault but it would tell you if the watchdog (window or independent) had tripped.

Rhianna answered 17/2, 2015 at 5:2 Comment(0)
R
2

When you say reset I think in terms of you hit the reset vector, not one of the interrupts or handlers. Are you saying that it does indeed reset the chip and start your software over again or are you saying that it is hanging somewhere? or do you have the vector table all point at the reset vector?

How to proceed depends on what you are really seeing, you need to be more clear or specific, or maybe you want help figuring that out.

Normally I map the unused vectors to a simple hang line of code which branches to itself. Later I may remap some of them to real code.

the cortex-m is very nice in that you can point at C code. If you think you are getting an exception have it point at a routine that grabs something that helps you figure out what mode you are in, the link register might have that info, or a csr somewhere, print that out and go into an infinite loop. Fill up the unused portions of the vector table with the address to this generic debug function.

From there you need to figure out why you are hitting that exception, it could be something like an unaligned access for example. It could be that you have generated an interrupt when trying to initalize a device before completely setting up the handler, who knows.

edit your question with more answers or information as you work through this.

Rossuck answered 10/1, 2012 at 18:48 Comment(0)
M
1

Given that you don't have a debugger, I would suggest that you find some peripheral on the microcontroller to help you. Perhaps you have an LED you can toggle or a simple GPIO pin that isn't being used that you can hook up to an oscilloscope. If you toggle a GPIO pin slowly enough (no faster than 1 Hz and maybe more slowly depending on the meter) you can use a volt meter instead of a scope. Put the code to toggle the LED or GPIO pin in each of the exception handlers one at a time until you track it down. If you have more than one GPIO pin available you can speed up the process. You can also write a wrapper for the specific function that is causing the reset. The wrapper function would send a list of interrupts that are enabled just before the breaking function is executed. This way you don't have to waste time testing the ones that are not enabled.

One advantage of GPIO pins in this case is they don't require an interrupt. It is best to stay away from anything that requires an interrupt (like your USART in this case). If the reset is being caused by a higher priority exception your debugging code will never execute.

It is also common that the reset is caused by an uninitialized pointer. A function pointer set to zero would cause execution to look a lot like a reset. If this is the case, the USART initialization code is probably being executed before a byte can be fully transmitted by the USART which would render the USART useless as a debugging tool in this instance.

Medrek answered 10/1, 2012 at 16:38 Comment(0)
F
1

You can use below code for debugging.

void HardFault_Handler(void)
{
    __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"
       );

  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

Also add this as well.

void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress )
{
    /* These are volatile to try and prevent the compiler/linker optimising them
    away as the variables never actually get used.  If the debugger won't show the
    values of the variables, make them global my moving their declaration outside
    of this function. */
    volatile uint32_t r0;
    volatile uint32_t r1;
    volatile uint32_t r2;
    volatile uint32_t r3;
    volatile uint32_t r12;
    volatile uint32_t lr; /* Link register. */
    volatile uint32_t pc; /* Program counter. */
    volatile uint32_t psr;/* Program status register. */

    r0 = pulFaultStackAddress[ 0 ];
    r1 = pulFaultStackAddress[ 1 ];
    r2 = pulFaultStackAddress[ 2 ];
    r3 = pulFaultStackAddress[ 3 ];

    r12 = pulFaultStackAddress[ 4 ];
    lr = pulFaultStackAddress[ 5 ];
    pc = pulFaultStackAddress[ 6 ];
    psr = pulFaultStackAddress[ 7 ];

    /* When the following line is hit, the variables contain the register values. */
    for( ;; );
}

I am using this to get any value of the register before going into hardfault. You can also add more registers if you like.

Freedafreedman answered 9/4, 2018 at 16:19 Comment(0)
E
0

The "right" thing to do, is, unfortunately not practical with an STM32. That would be to put in a big exception handler that has knowledge of the source code, and can unwind the stack and give you the full call stack and line number that cause the fault. This would require adding all the debug information from your application into the flash memory of the STM32, and that's not practical.

There are ways of tricking your IDE to sometimes give you the call stack. I'd give details but I forgot to write them down, so I've forgotten. I think it has to manually changing the stack pointer from one shadow register to another.

What I usually do is to put a breakpoint at the hard fault exception vector, and then look at all the registers when the break point hits. Consider them forensic evidence from a murder, done with plastic explosives. The values of the registers will give you ideas. Register values that start with 0x20000000 are RAM addresses. Register values that start with 0x08000000 are Flash addresses. Open up the disassembler and type in those addresses. It will probably go straight to the variable or function at those memory locations. If that doesn't help, then look at the stack pointer. Look at the memory locations at the stack pointer, and do the same trick. I have always found enough shrapnel to location the function where the exception was happening.

Eulau answered 7/2, 2012 at 1:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.