This answer is general to processors and peripherals, and has an SRAM specific comment at the end, which is probably pertinent to your specific RAM and CPU.
Output pins can be driven in three different modes:
- open drain - a transistor connects to low and nothing else
- open drain, with pull-up - a transistor connects to low, and a resistor connects to high
- push-pull - a transistor connects to high, and a transistor connects to low (only one is operated at a time)
Input pins can be a gate input with a:
- pull-up - a resistor connected to high
- pull-down - a resistor connected to low
- pull-up and pull-down - both a resistor connected to high and a resistor connected to low (only useful in rare cases).
There is also a Schmitt triggered input mode where the input pin is pulled with a weak pull-up to an initial state. When left alone it persists in its state, but may be pulled to a new state with minimal effort.
Open drain is useful when multiple gates or pins are connected together with an (external or internal) pull-up. If all the pin are high, they are all open circuits and the pull-up drives the pins high. If any pin is low they all go low as they tied together. This configuration effectively forms an AND
gate.
_____________________________
Note added November 2019 - 7+ years on: The configuration of combining multiple open collector/drain outputs has traditionally been referred to as a "Wired OR" configuration. CALLING it an OR (even traditionally) does not make it one. If you use negative logic (which traditionally may have been the case) things will be different, but in the following I'll stick to positive logic convention which is what is used as of right unless specifically stated.
The above comment about forming an 'AND' gate has been queried a number of times over the years - and it has been suggested that the result is 'really' an 'OR' gate. It's complex.
The simple picture' is that if several open collector outputs are connected together then if any one of the open collector transistors is turned on then the common output will be low. For the common output to be high all outputs must be off.
If you consider combining 3 outputs - for the result to be high all 3 would need to have been high individually. 111 -> 1. That's an 'AND'.
If you consider each of the output stages as an inverter then for each one to have a high output it's input must be low. So to get a combined high output you need three 000 -> 1 . That's a 'NOR'.
Some have suggested that this is an OR - Any of XYZ with at least 1 of these is a 1 -> 1.
I can't really "force" that idea onto the situation.
_________________________________
When driving an SRAM you probably want to drive either the data lines or the address lines high or low as solidly and rapidly as possible so that active up and down drive is needed, so push-pull is indicated. In some cases with multiple RAMs you may want to do something clever and combine lines, where another mode may be more suitable.
With SRAM with data inputs from the SRAM if the RAM IC is always asserting data then a pin with no pull-up is probably OK as the RAM always sets the level and this minimises load. If the RAM data lines are sometimes open circuit or tristate you will need the input pins to be able to set their own valid state. In very high speed communications you may want to use a pull-up and a pull-down so the parallel effective resistance is the terminating resistance, and the bus idle voltage is set by the two resistors, but this is somewhat specialist.
Have a look at Section 9.1.4 on page 157:
9.1.4 Alternate functions (AF)
It is necessary to program the Port Bit Configuration Register before
using a default alternate function.
- For alternate function inputs, the port must be configured in Input mode (floating, pullup or pull-down) and the input pin must be driven
externally.
Note: It is also possible to emulate the AFI input pin by software by programming the GPIO controller. In this case, the port should be
configured in Alternate Function Output mode. And obviously, the
corresponding port should not be driven externally as it will be
driven by the software using the GPIO controller.
For alternate function outputs, the port must be configured in Alternate Function Output mode (Push-Pull or Open-Drain).
For bidirectional Alternate Functions, the port bit must be configured in Alternate Function Output mode (Push-Pull or
Open-Drain). In this case the input driver is configured in input
floating mode
If you configure a port bit as Alternate Function Output, this
disconnects the output register and connects the pin to the output
signal of an on-chip peripheral.
If software configures a GPIO pin as Alternate Function Output, but
peripheral is not activated, its output is not specified.
If a pin or a bus is supposed to be bidirectional for an Alternate Function, configure it as an Alternate Function in Output mode and the input configuration happens automatically.
If the pin is supposed to be an input for an Alternate Function, configure it in Input Mode.
If the pin is supposed to be an output for an Alternate Function, configure it in Output mode.
When you're using a pin as GPIO, you have to choose between Input mode or Output mode.
Best Answer
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
toGPIO_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.