cancel
Showing results for 
Search instead for 
Did you mean: 

BUG in STM32F1xx_ll_gpio driver (STM32Cube_FW_F1_V1.8.3) in regards to Push/Pull resistor settings on PUSHPULL OUTPUT

TCamu.2
Associate

Dear readers,

It has taken me quit a lot of time and rewrites to get this working (using STMCUBE IDE V1.5 and LL libraries V1.8.3, for a STM32F103VB).

When initializing the GPIO pins for PUSHPULL outputs i want to make sure that the pins initialize with the correct output level.

So before i call LL_GPIO_INIT( GPIOD, &GPIO_InitStruct ) i call LL_GPIO_SetOutputPin( GPIOD, _ERROR_Pin );

However after the INIT the pins are LOW even after i set them (to HIGH).

The way i see it:

  • in the LL_GPIO_Init() function something goes wrong

This functions (always) calls:

     LL_GPIO_SetPinPull(GPIOx, currentpin, GPIO_InitStruct->Pull); to configure pull up or down resistors, that does not make much sense if we have a push pull output configuration, but should not be a problem, however, this function does not write to the (input) push pull settings (in the CRL / CNFx registers) it writes to the ODR register (setting or resetting the output state): 

__STATIC_INLINE void LL_GPIO_SetPinPull(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Pull)
{
  MODIFY_REG(GPIOx->ODR, (Pin >> GPIO_PIN_MASK_POS), Pull << (POSITION_VAL(Pin >> GPIO_PIN_MASK_POS)));
}

Running the following code and stepping through it while viewing the GPIOD ODR SFRs clearly shows the ouput pin being set (by LL_GPIO_SetOutputPin()) and next being reset (by LL_GPIO_SetPinPull())

#define _HEARTBEAT_Pin 			LL_GPIO_PIN_0
#define _ERROR_Pin 			LL_GPIO_PIN_1
LL_GPIO_InitTypeDef 		GPIO_InitStruct		= {0};
 
// GPIO Ports Clock Enable
LL_APB2_GRP1_EnableClock( LL_APB2_GRP1_PERIPH_GPIOC );
LL_APB2_GRP1_EnableClock( LL_APB2_GRP1_PERIPH_GPIOD );
LL_APB2_GRP1_EnableClock( LL_APB2_GRP1_PERIPH_GPIOE );
 
LL_GPIO_SetOutputPin( GPIOD, _ERROR_Pin );	// does NOT WORK prior to LL_GPIO_INIT
LL_GPIO_SetOutputPin( GPIOD, _HEARTBEAT_Pin );		// does NOT WORK prior to LL_GPIO_INIT
GPIO_InitStruct.Pin		= _HEARTBEAT_Pin | _ERROR_Pin;
GPIO_InitStruct.Mode		= LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed		= LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType	= LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init( GPIOD, &GPIO_InitStruct );

When i (temporarily) disable the call for LL_GPIO_SetPinPull() in LL_GPIO_Init() it works as intended.

My suggestion / solution (for now) is as follows:

  • In LL_GPIO_Init() change the call to SetPinPull() like this:
if( GPIO_InitStruct->OutputType != LL_GPIO_OUTPUT_PUSHPULL )		// Skip pull up or down resistor setting if OutputType == LL_GPIO_OUTPUT_PUSHPULL
	LL_GPIO_SetPinPull( GPIOx, currentpin, GPIO_InitStruct->Pull );	// Pull-up Pull-down resistor configuration
 
LL_GPIO_SetPinMode( GPIOx, currentpin, GPIO_InitStruct->Mode );	// Pin Mode configuration

The real solution would be to change the LL_GPIO_SetPinPull() routine but i assume it works for inputs (i have not tested that) so i left that alone for now.

Am i the only one having this problem and am i doing something wrong or is this a bug?

  •  
3 REPLIES 3
TDK
Guru

Not really a bug in the LL library, per se.

Unlike other families, the STM32F1 family cannot enable a pullup/down in output mode.

0693W000006ElZ5QAK.png 

When in input mode, the pullup vs pulldown is selected using the ODR register. So it's modifying the right register.

The real issue is that you're calling a function which is cannot be called while in output mode. See the following table for your options:

0693W000006ElZAQA0.png 

CubeMX seems to let you set the pullup or pulldown while in output mode, which is a bug. Trying to do so will probably generate the incorrect code you're using.

Edit:

If you want low-level access, dump the LL library and modify registers directly. It will be much more productive and you won't be tied to a library which may or may not change in the future. Registers will never change. As you've found, debugging the LL library requires you to not only know the LL library, but also the actual registers, so it doesn't save work.

If you feel a post has answered your question, please click "Accept as Solution".
TCamu.2
Associate

Hello TDK,

I agree with you and have read the the data sheets, the problem is the (current) implementation in the LL GPIO driver. The 'weak pull up/down resitors are disabled' part is (in my opinion) wrongly implemented in the mentioned driver.

Even if you don't actually set pull up or down it handles as if you set the 'default' (0) value.

Yes i could write even lower level... code then the LL... but i'm to lazy (most of the time) and run into problems like this...

I have written my own version of the init function by now (with some more tweaks), but by reporting it like this i hope to 'inspire' someone@ST.com to improve the drivers, so others don't have to...

TDK
Guru

Okay, I agree with what you're saying. The push/pull value is either pullup or pulldown, with no option for no pull. Clearly a bug.

#define IS_LL_GPIO_PULL(__VALUE__)         (((__VALUE__) == LL_GPIO_PULL_DOWN)   ||\
                                            ((__VALUE__) == LL_GPIO_PULL_UP))

If you do choose "No pull", the CubeMX code simply doesn't initialize this parameter, which typically means it'll be 0 and act as if it were set to pulldown (since the whole structure is initialized to zero).

If you feel a post has answered your question, please click "Accept as Solution".