In this STM32L486xx HAL User Manual (note: 36 MB!) is an example for GPIO configuration to power an external LED on page 53. The code lines that are confusing me are these two:

GPIO_InitStruct.Pull = GPIO_PULLUP; 

So they are using push-pull mode in combination with a pullup. Why would you do that? I can't think of a use case. Either the output is high anyway, then the pullup won't be necessary. Or the output is low, than the pullup will stand no chance and the output signal will still be low, with or without pull up. So is there a reasonable use for this configuration?

  • 1
    \$\begingroup\$ It isn't a good example to begin with, since they never say how the LEDs would actually be connected to the pins. So any explanation we give here would be pure speculation. \$\endgroup\$ – Dave Tweed Oct 4 '19 at 12:18
  • 1
    \$\begingroup\$ In the code, does the unit ever enter standby mode? \$\endgroup\$ – Peter Smith Oct 4 '19 at 12:39

Just like the pull-up, the reason to do this is a bit weak. Is it wrong to do this? Certainly not. Should you do this though? Possibly. Does it make much sense for a code example that is doing nothing but flashing an LED? Not really.

This series of ST microcontrollers have their pull-up and pull-down resistors controlled completely independently from anything else, allowing you to indeed do current-wasting configurations like this one, where the pull-up will do nothing when the pin is high, and will waste maybe a hundred µA or two of current (still without doing anything) when low.

There is a reason one might wish to do this, and it is simply to ensure the pin will be pulled to a default state if the mode is changed away from push-pull mode later. While it is true that neither the pull-up or pull-down resistors serve any purpose in push-pull mode, the pull-up resistor at least is likely desirable in all other modes: open-drain, and input. So if you aren't counting every µA, and might be changing the roles of certain pins, it can make sense to leave the pull-up turned on all the time, just to ensure that the pin will never get floated by accident or otherwise glitch when the pin mode of the GPIO pin is switched to something other than push-pull. Indeed, any time you had a pin in push-pull mode and needed to switch it to open-drain or an input mode, you would probably activate the pull-up resistor before actually switching to that mode, so the ability to have the pull-up resistor turned on even when in push-pull mode is certainly useful and important. It is needed to prevent glitching when changing the pin to input or open drain, depending on what is connected to it of course.

Now, it is definitely an odd choice to use it in an example like they have. Just to be absolutely clear, one could (and arguably should in most cases) set GPIO_InitStruct.Pull to GPIO_NOPULL when using the pin in push-pull mode.

Of course, if you do that, you will have to remember to turn on a pull-up or pull-down resistor (if there isn't an external one) should you change the pin mode to something other than push-pull. Perhaps ST is just playing it safe and assuming someone blinking an LED will stand a good chance of overlooking this, at least initially. Or maybe it was just a small oversight. Regardless, it certainly serves no real purpose in the example shown and because of that, I feel it mostly just confuses the point of the documentation here.

Which is why you've come to stack exchange to even ask this question. Maybe someone from ST will update the PDF after reading this. In my opinion, they should, as it is clearly confusing people and that is the last thing one would want documentation to do.

Beyond that, don't read too much into it. You'll run across little oddities like this fairly regularly, but don't start second guessing yourself each time it happens. I think most of us probably like to pretend all our datasheets and documentation and application notes are error-free, with every line of example code and every sentence purposeful and well thought-out. But we're all human, and as long as humans are still writing datasheets and documentation, sometimes you'll find mistakes. Many mistakes you'll come across are relatively benign and result in something that is merely a little strange or unnecessary like this example. These are also the ones that often never get corrected, simply because it doesn't really matter that much and no one can be bothered.

How I handled specific code oddity, and a good way to handle something like this in the future is to consult the actual datasheet of the chip in question. Understand how the port physically works, and then you'll be confident enough that there isn't really a reason to turn on the pull-up in that situation and not worry about it. If you understand how the chip functions, that's all you need. You'll be able to spot, rather than be at the mercy of, any weirdness contained in any example code for the chip, regardless of who wrote it.


From STM32L4x5 and STM32L4x6 RM0351 - Reference manual.

A GPIO can have the following modes:

  • Input floating
  • Input pull-up
  • Input pull-down
  • Analog
  • Output open-drain with pull-up or pull-down capability
  • Output push-pull with pull-up or pull-down capability
  • Alternate function push-pull with pull-up or pull-down capability
  • Alternate function open-drain with pull-up or pull-down capability

Weak 40kΩ pull-ups and pull-downs make no sense for a push/pull output capable of sinking/sourcing 25mA (impact is only 0.125mA at 5V). But this assumes the GPIO pin will not change it's mode.

Any GPIO pin can transition from one mode to another. An input can become an output and vice versa. Bidirectional and multi purpose pins.

Note all the output and alternate modes have pull-up and pull-down capability, while input is divided into three categories (floating, pull-up and pull-down). Why would you need an open-drain output with pull-down capability?

To avoid GPIOs floating as a pin transitions from output to input, pull-ups and pull-downs are provided on all outputs to set inputs to a default or off state.

If the GPIO pin will not ever change it's mode, configure with NOPULL.


You wouldn't do this during normal use. But if you want to use the deepest sleep modes, IO pins can be disabled and the pull-ups and pull-downs keep the state.

Or if you just want to use open-drain communication with internal pull-up.

  • \$\begingroup\$ This doesn't address the OP's question, it answers about what their question isn't. Downvoting, I'm afraid. \$\endgroup\$ – TonyM Oct 2 '19 at 11:06

This has to do with the GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; statement (just below your quoted code).

Looking deep into the reference manual (on page 296):

STM32L486 GPIO Speed fields

GPIO_SPEED_FREQ_MEDIUM has a value of 01 in the actual register (see page 304 in the reference manual) and this is achieved by loading the output pin with pullup (other modes either load with a pulldown or no load).

Just why that could not have been part of the HAL (medium speed is known to require a pullup) is not clear as the library should just know that although this part of the HAL is generic and other parts in the various STM32 series may not need to pull the pin (or not the same way) to achieve this speed and it therefore has to be explicitly stated.

So it is to actually achieve the medium speed function.

I will note that modern microcontroller documentation contains so much information that it can easily take weeks (or months) to completely understand all the internal parts and nuances.

  • 4
    \$\begingroup\$ No, you are confusing the roles of distinct registers, which use similar constants but for entirely distinct purposes. \$\endgroup\$ – Chris Stratton Oct 4 '19 at 14:26
  • 3
    \$\begingroup\$ Indeed. @ChrisStratton is correct. The speed register is address offset 0x08, and the pull-up/pull-down register is address offset 0x0C. This answer can't possibly be correct, as the values are being written to completely different places in memory (different registers). Source: the very page, 304, of the document you referenced. \$\endgroup\$ – metacollin Oct 5 '19 at 8:11
  • \$\begingroup\$ My point is that the speed dictates the pull up which does *not * appear to be handled by the library. Therefore these bits are being set up by the user code. \$\endgroup\$ – Peter Smith Oct 5 '19 at 14:37
  • 4
    \$\begingroup\$ No, the speed does not dictate the pull up. There is nothing in your post but mistaken confusion, so you should delete it to avoid misleading less experienced readers. \$\endgroup\$ – Chris Stratton Oct 5 '19 at 15:07
  • 4
    \$\begingroup\$ If you study the green block you highlighted, you will see that speed is determined by OSPEED[0] and OSPEED[1] exclusively within that block. OTYPER sets the output type to either push/pull or open drain. PUPD[1] and PUPD[0] set the pullup. So your answer really is totally wrong. You are misunderstanding the purpose of table 38. Table 38's main purpose is to explain the effects of various configuration registers for all the various cases of alternate function, input vs output, pullup or pulldown, etc. \$\endgroup\$ – mkeith Oct 5 '19 at 18:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.