Author Topic: Timer1 only works when handle is on stack vs. static memory on STM32F1x  (Read 1755 times)

0 Members and 1 Guest are viewing this topic.

Offline uer166Topic starter

  • Frequent Contributor
  • **
  • Posts: 890
  • Country: us
Consider setup code for timer1 + PWM:

if TIM_HandleTypeDef (first line in function) is allocated as static, or outside the function, the complementary output stops working, wtf??

Code: [Select]
void setup_timer()
{
        TIM_HandleTypeDef TIM_HandleStruct;

TIM_OC_InitTypeDef TIM_OC_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
TIM_BreakDeadTimeConfigTypeDef TIM_DeadTimeStruct;

TIM_HandleStruct.Instance = TIM1;
TIM_HandleStruct.Channel = HAL_TIM_ACTIVE_CHANNEL_1;
TIM_HandleStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM_HandleStruct.Init.Prescaler = 31; // 2.064516 MHz
TIM_HandleStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM_HandleStruct.Init.Period = 255; // more or less 8 bit PWM
TIM_HandleStruct.Init.RepetitionCounter = 0;
TIM_HandleStruct.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

if(HAL_TIM_PWM_Init(&TIM_HandleStruct) != HAL_OK)
{
asm("bkpt 255");
}

TIM_OC_InitStruct.OCMode = TIM_OCMODE_PWM1;
TIM_OC_InitStruct.Pulse = 10;
TIM_OC_InitStruct.OCPolarity = TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = TIM_OCNPOLARITY_HIGH;
TIM_OC_InitStruct.OCIdleState = TIM_OCIDLESTATE_RESET;
TIM_OC_InitStruct.OCNIdleState = TIM_OCNIDLESTATE_RESET;

if(HAL_TIM_PWM_ConfigChannel(&TIM_HandleStruct, &TIM_OC_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
asm("bkpt 255");
}

GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

if(HAL_TIM_Base_Start(&TIM_HandleStruct) != HAL_OK)
{
asm("bkpt 255");
}

TIM_DeadTimeStruct.DeadTime = 70;

if(HAL_TIMEx_ConfigBreakDeadTime(&TIM_HandleStruct, &TIM_DeadTimeStruct) != HAL_OK)
{
asm("bkpt 255");
}

if(HAL_TIM_PWM_Start(&TIM_HandleStruct, TIM_CHANNEL_1) != HAL_OK)
{
asm("bkpt 255");
}

if(HAL_TIMEx_PWMN_Start(&TIM_HandleStruct, TIM_CHANNEL_1) != HAL_OK)
{
asm("bkpt 255");
}
__HAL_TIM_SET_COMPARE(&TIM_HandleStruct, TIM_CHANNEL_1, 240);

}
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 9946
  • Country: nz
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #1 on: August 22, 2018, 07:35:12 am »
This doesnt surprise me much.
The standard peripheral library is a POS.
It does nothing except make things harder.
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4228
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #2 on: August 22, 2018, 07:40:19 am »
I'm afraid I have to agree, ST's HAL performs obfuscation, not abstraction.

Nevertheless, if the code works (or not) depending on how variables are allocated, then you should be able to see a difference in the disassembly.

I don't really see an alternative to going through the assembly code to see what is actually going on when you call the various HAL functions, and see what's getting set incorrectly and where.

You might possibly get closer to the problem sooner by examining the contents of the timer registers in each of the two cases, and just exploring the code that sets whichever register(s) end up different in the working vs non-working cases.

Offline uer166Topic starter

  • Frequent Contributor
  • **
  • Posts: 890
  • Country: us
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #3 on: August 22, 2018, 07:58:22 am »
Ah! This is strictly my screw-up. On the stack the handle struct happened to initialize to 0 on all fields. I unknowingly didn't initialize all fields, and was relying on the 0 initial state. When put in static memory it wasn't zeroed-out, and  had some random crap, breaking it.

@Andy, found thanks to advice of checking all registers (or in this case the struct at initialization).
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #4 on: August 22, 2018, 08:33:25 am »
Ah! This is strictly my screw-up. On the stack the handle struct happened to initialize to 0 on all fields. I unknowingly didn't initialize all fields, and was relying on the 0 initial state. When put in static memory it wasn't zeroed-out, and  had some random crap, breaking it.
Memory allocated on stack isn't implicitly initialized. If it happened to work, it was purely by chance. Statically allocated memory, on the other hand, is initialized to all zeroes, unless you are using some horribly broken compiler.
 
The following users thanked this post: newbrain

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #5 on: August 22, 2018, 08:54:41 am »
Ah! This is strictly my screw-up. On the stack the handle struct happened to initialize to 0 on all fields. I unknowingly didn't initialize all fields, and was relying on the 0 initial state. When put in static memory it wasn't zeroed-out, and  had some random crap, breaking it.
Memory allocated on stack isn't implicitly initialized. If it happened to work, it was purely by chance. Statically allocated memory, on the other hand, is initialized to all zeroes, unless you are using some horribly broken compiler.

Latest Embedded Muse newlsletter addressed this :)

The MISRA rules don't address this substantially, though rule 9.3 states: "Arrays shall not be partially initialized." The Barr Group standard's rule 7.2.a is "All variables shall be initialized before use". I quickly (maybe too quickly) looked through the C99 standard and found at 5.1.2 "All objects with static storage duration shall be initialized (set to their initial values) before program startup. The manner and timing of such initialization are otherwise unspecified." It's not at all clear to me that this means set to zero. 6.2.4 appears to indicate that arrays are not initialized: "The initial value of the object is indeterminate." Richard Man of Imagecraft, an embedded compiler vendor, said all C-compliant compilers set the BSS to zero, but mentioned that sometimes developers do tricky things in the startup code that might cause problems.

As an old-timer who grew up on assembly language my practice has always been to initialize every variable prior to first use, regardless of language. One could zero out the entire BSS in the startup code, but I find an explicit assignment statement clearer.

Then i suppose it depends, on the compiler.
For example, XC16 (which is GCC) has a linker option to initialize data sections (variables in use) whereas XC8 (which is not GCC, and its targets are 8bit PIC with relatively low ram) has a linker option to initialize bss and/or the rest data memory, so everything.
When selected, variables that are explicitely initialized are initialized to their values, variables that are not explicitely initialized are initialized to zero.
Local variables that are in the CPU registers are not initialized, elements added to the stack are not initialized. The initialization only happens at startup, when static memory is allocated
« Last Edit: August 22, 2018, 08:56:20 am by JPortici »
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #6 on: August 22, 2018, 09:01:21 am »
Initializing this trivial timer/PWM "by hand" is maybe 4 to 5 lines of simple code with no stack variables nor static variables required at all, so much less footprint for mistakes like that.

This code is obfuscated beyond readable and indeed prone to mistakes.
 

Offline obiwanjacobi

  • Frequent Contributor
  • **
  • Posts: 988
  • Country: nl
  • What's this yippee-yayoh pin you talk about!?
    • Marctronix Blog
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #7 on: August 22, 2018, 01:35:09 pm »
put a
Code: [Select]
memset(&TIM_HandleStruct, 0, sizeof(TIM_HandleTypeDef )); in the function an you'll know if its an initialization issue.
Arduino Template Library | Zalt Z80 Computer
Wrong code should not compile!
 

Offline nano

  • Contributor
  • Posts: 29
  • Country: de
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #8 on: August 22, 2018, 02:35:49 pm »
Memory allocated on stack isn't implicitly initialized. If it happened to work, it was purely by chance. Statically allocated memory, on the other hand, is initialized to all zeroes, unless you are using some horribly broken compiler.

Exactly!
Just as an additional information: For Cortex-M processors it’s not actually the compilers responsibility.
You have to make sure to include the proper CMSIS startup files (as supplied by the chip manufacturer) in your projects.
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Timer1 only works when handle is on stack vs. static memory on STM32F1x
« Reply #9 on: August 22, 2018, 02:59:29 pm »
Latest Embedded Muse newlsletter addressed this :)

[...]
I quickly (maybe too quickly) looked through the C99 standard
[...]
Yes, maybe too quickly. I would not have expected this from him...
From the C99 draft (not changed in the standard AFAICR), ch 6.7.8, clause 10 in "Semantics":
Quote
If an object that has automatic storage duration is not initialized explicitly, its value is
indeterminate. If an object that has static storage duration is not initialized explicitly,
then:
— if it has pointer type, it is initialized to a null pointer;
— if it has arithmetic type, it is initialized to (positive or unsigned) zero;
— if it is an aggregate, every member is initialized (recursively) according to these rules;
— if it is a union, the first named member is initialized (recursively) according to these
rules.

Of course, in an embedded environment the zeroing and initializing task is carried out by the startup code, as nano correctly points out.
Nandemo wa shiranai wa yo, shitteru koto dake.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf