2020-12-06 09:59 AM
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:
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:
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?
2020-12-06 10:26 AM
Not really a bug in the LL library, per se.
Unlike other families, the STM32F1 family cannot enable a pullup/down in output mode.
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:
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.
2020-12-06 10:40 AM
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...
2020-12-06 03:39 PM
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).