cancel
Showing results for 
Search instead for 
Did you mean: 

Strange MCU behaviour by passing pointer to stucture

Tom
Associate II
Posted on March 24, 2014 at 13:44

Hi All,

I'm running some code on Discovery STM32L152 board. I'm getting Hard Fault each time I initiate structure fields in a function:

1.
void pid_Init(float p_factor, float i_factor, float d_factor, pidData_t *pidPtr)
2.
{
3.
pidPtr->lastProcessValue = 0;
4.
pidPtr->sumError = 0;
5.
}

where:

typedef struct {
int16_t lastProcessValue;
int32_t sumError;
float P_Factor;
float I_Factor;
float D_Factor;
int16_t maxError;
int32_t maxSumError;
} pidData_t;

I call the fuction by:

pid_Init(K_P,K_I,K_D,&pidData);

While debugging I noticed that: The .map file shows that pidData is at address: 0x200034c0 when I step into line 3. of

pid_Init

I see that

*pidPtr

=0x3fa99999 which is way beyond the actual pidData address in SRAM. In this situation

pidPtr->lastProcessValue

command of course causes Hard Fault. How is it possible that the pidData address passed to pid_Init is being set to some 0x3fa99999 value ??? I though about the stack size but it is set to:

1.
/*----------Stack Configuration-----------------------------------------------*/ 
2.
#define STACK_SIZE 0x00000500 /*!< Stack size (in Words) */
3.
__attribute__ ((section(''.co_stack'')))
4.
unsigned long pulStack[STACK_SIZE];

and increasing its size brings nothing Also I should have enough SRAM as the linker says:

1.
Program Size:
2.
182488 10328 9192 202008 31518 DiscoveryCooCox.elf
3.
text data bss dec hex filename
4.
5.
BUILD SUCCESSFUL
6.
Total time: 3 seconds

I've spent 2 days on that and already ran out of ideas and patience. Can anyone help please? Regards Tom #pointer-to-structure
8 REPLIES 8
stm322399
Senior
Posted on March 24, 2014 at 14:02

Tom,

assuming that pidData is statically allocated (global variable, isn't it?), it can not change it's address.

I suspect the problem lies in space reserved in register/stack to pass floats to pid_Init. Can you try for a test to move the declaration of pidPtr argument at the first position in pid_Init ?

Posted on March 24, 2014 at 14:05

I'm getting a confusing mix of descriptions and code fragments, please condense this into a complete/concise example that is compilable.

Provide a disassembly of the subroutine and processor registers at the fault.

Do you have the ability to using printf() via a serial/debug terminal?

*pidPtr reads the value stored at pidPtr, what does pidPtr read as?

printf(''%08lX %08lX\n'', (unsigned long)pidPtr, (unsigned long)*pidPtr);

At the entry to pid_Init(), what is in R4
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Tom
Associate II
Posted on March 24, 2014 at 14:57

Hello Laurent!

You are right! I rearranged the input variables:

void pid_Init(pidData_t *pidPtr,float p_factor, float i_factor, float d_factor)

and I'm no longer getting Hard Fault! The address passed in R4 is 0x200034ec which is correct.

0800d1d6: nop 
70 {
pid_Init:
0800d1d8: push {r3, r4, r5, r6, r7, lr}
0800d1da: mov r4, r0
0800d1dc: mov r7, r2
73 pidPtr->lastProcessValue = 0;
0800d1de: movs r6, #0
0800d1e0: strh r6, [r0, #0]
74 pidPtr->sumError = 0;
0800d1e2: str r6, [r0, #4]
77 pidPtr->P_Factor = p_factor;
0800d1e4: str r1, [r0, #8]
79 pidPtr->I_Factor = i_factor;
0800d1e6: str r2, [r4, #12]
80 PID_I_On=0;
0800d1e8: movw r5, #10356 ; 0x2874
0800d1ec: movt r5, #8192 ; 0x2000
0800d1f0: strb r6, [r5, #0]
82 pidPtr->D_Factor = d_factor;
0800d1f2: str r3, [r0, #16]
88 pidPtr->maxError = MAX_INT / (pidPtr->P_Factor + 1);
0800d1f4: mov r0, r1
0800d1f6: mov.w r1, #1065353216 ; 0x3f800000
0800d1fa: bl 0x800dc6c <
__aeabi_fadd
>
0800d1fe: mov r1, r0
0800d200: mov.w r0, #65024 ; 0xfe00
0800d204: movt r0, #18175 ; 0x46ff
0800d208: bl 0x800dfe4 <
__divsf3
>
0800d20c: bl 0x800e11c <
__fixsfsi
>
0800d210: strh r0, [r4, #20]
89 pidPtr->maxSumError = MAX_I_TERM / (pidPtr->I_Factor + 1);
0800d212: mov r0, r7
0800d214: mov.w r1, #1065353216 ; 0x3f800000
0800d218: bl 0x800dc6c <
__aeabi_fadd
>
0800d21c: mov r1, r0
0800d21e: mov.w r0, #1317011456 ; 0x4e800000
0800d222: bl 0x800dfe4 <
__divsf3
>
0800d226: bl 0x800e11c <
__fixsfsi
>
0800d22a: str r0, [r4, #24]
0800d22c: pop {r3, r4, r5, r6, r7, pc}

and the regs:

Core 
r0 0x200034ec 
r1 0x402e0000 
r2 0x00000000 
r3 0x40000000 
r4 0x200034ec 
r5 0x40000000 
r6 0x00000000 
r7 0x00000000 
r8 0x2000289b 
r9 0x2000287c 
r10 0x40020400 
r11 0x20002894 
r12 0x01010101 
sp 0x20004bf8 
lr 0x08000655 
pc 0x0800d1e0 
xpsr 0x61000000 

How can I increase the space reserved in register/stack to pass these floats ? I could use another structure for the floats and pass pointer but I wouldn't like to modify the source too much as I move a large code from AVR to ARM and don't want to mess it up. I'm using CooCox and I'm not quite good at setting up stacks, heaps etc.... I rely on standard settings so far. Regards Tom
Posted on March 24, 2014 at 15:05

The ABI should permit 4 32-bit parameters to be passed by register, beyond that the stack would be used.

Not sure why this would be a problem unless GNU/GCC is broken. Then you'd want to review the version of the compiler being used, and if this is a known issue.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
stm322399
Senior
Posted on March 25, 2014 at 09:37

Tom,

this generated code looks good. the pointer goes into R0 and the three floats each 32-bit stored into R1, R2, R3. (R4 has nothing todo with this parameter passing stuff. Clive1 certainly wanted you to look at R3 to find the value of the pointer).

Anyway, to have a successful call, the caller and the callee must agree on parameter passing, and in particular on the way to pass floats (aren't they declared as doubles in the caller ???). Can you show us the caller disassembly (the better is to show us disassembly from the version that made the hard fault).

Does caller and callee are compiled from the same source file or from distinct source file. In the latter case what is the prototype declared for Pid_init ?

Tom
Associate II
Posted on March 25, 2014 at 11:47

Hello guys,

You were right again 🙂

pid_Init

was called in the file A but defined in the file B. The problem was I didn't declare a prototype in file A and was getting the ''implicit declaration'' warning which I ignored as I thought the compiler would do the job anyway and would throw an error if it was a major problem (at least my previous IDE wouldn't have let it go as a warning). It looks caller/callee mechanizm worked improperly due to the implicit declaration of the compiler? Regards Tom
stm322399
Senior
Posted on March 25, 2014 at 14:32

Yes, so was my bet. Let's suppose the caller makes the calls passing doubles, so the compiler implicitly think that the target function takes doubles as arguments and pass them appropriately (they are 64-bit value each).

Will you have declared a prototype of PidInit, the compiler will have done an implicit cast, and pass arguments as floats (32-bit each).

Tom
Associate II
Posted on March 25, 2014 at 15:15

I see one has to be very carefull with gcc and take warnings as seriously as errors.

Many thanks for your help.

Topic closed.

Regards

Tom