2
\$\begingroup\$

Hi I'm currently attempting to write a simple program using the STM32 Nucleo development board, based around the STM32F334R8. The application sets up the GPIO ports A and C and reads the state of the button. If the button is pressed, it turns on the LED. If not, it turns it off.

From ST's documentation (STM Nucleo 64) I've verified that the button is wired to Pin C13 and LED2 is wired to Pin A5.

From the datasheet, I've been reading through the registers for the GPIO (Long STM32F334 Datasheet) and see there are three that seem to be related to the output; ODR, BSRR, and BRR.

When implementing the application mentioned above, my first thought was to write the GPIO pin state for the output LED using ODR, like so:

SET_BIT(GPIOA->ODR, GPIO_ODR_5);
CLEAR_BIT(GPIOA->ODR, GPIO_ODR_5);

This did not appear to work. My button presses were detected, as I could verify this in the debugger by seeing it follow different paths. Here's my setup code for the GPIO:

// set one to input
CLEAR_BIT(GPIOC->MODER, GPIO_MODER_MODER13_0);
CLEAR_BIT(GPIOC->MODER, GPIO_MODER_MODER13_1);
// set another to output
SET_BIT(GPIOA->MODER, GPIO_MODER_MODER5_0);
CLEAR_BIT(GPIOA->MODER, GPIO_MODER_MODER5_1);
// set output to push-pull
CLEAR_BIT(GPIOA->OTYPER, GPIO_OTYPER_OT_5);
// set input with pull-up
SET_BIT(GPIOC->PUPDR, GPIO_PUPDR_PUPDR13_1);
CLEAR_BIT(GPIOC->PUPDR, GPIO_PUPDR_PUPDR13_0);

// enable GPIOA and GPIOC clock
SET_BIT(RCC->AHBENR, RCC_AHBENR_GPIOAEN);
SET_BIT(RCC->AHBENR, RCC_AHBENR_GPIOCEN);

I also saw a piece of example code that worked on my board that uses the BSRR and BRR bits, like so:

// set output high
SET_BIT(GPIOA->BSRR, GPIO_BSRR_BS_5);
// set output low
SET_BIT(GPIOA->BRR, GPIO_BRR_BR_5);

... But this didn't work.

I'm not sure I understand the difference between the three registers and their functions.

What are the use cases for each? Which one is suited to my application?

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

The most obvious problem I see in your code is that you're writing to the GPIO registers before enabling the clocks for the appropriate peripherals.

Peripherals won't respond correctly if their clocks haven't been enabled. Enable all peripheral clocks before you try to do anything with the peripherals.

That all being said: the differences between ODR and BSRR/BRR are minimal. If you only need to change one bit, though, a single write to BSRR (or BRR) is faster, and uses less code space, than a read/modify/write cycle on ODR. However, if you actually do want to set the state of the whole port at once, or if you need to check the current output state, ODR is the way to go.

\$\endgroup\$
5
  • \$\begingroup\$ You are 100% right, moving the clock setup to the top of that function fixed the problem and the application works! It hadn't occurred to me that the clocks needed to be enabled first as I thought they would only govern the sampling of the pins, not enable the peripheral entirely which. Thanks! \$\endgroup\$ Oct 24, 2017 at 20:23
  • 1
    \$\begingroup\$ @ShaneSnover: One should generally avoid using ODR in any circumstances that would require a read-modify-write sequence, since an interrupt that tries to manipulate one port pin (whether via ODR or BSRR) while other code uses a read-modify-write on ODR to manipulate other pins will have its effects overwritten by the read-modify-write sequence. \$\endgroup\$
    – supercat
    Jun 17, 2020 at 22:09
  • \$\begingroup\$ @supercat thanks for this comment. This solved a major problem in my code. At very unconsistent, very random intervals (1 per day or so), my application would generate an error. Cause: Read-Modify-Write interrupted to perform another Read-Modify-Write on, you've guessed it, the same ODR register. This was very hard to debug, have been trying to find the cause for several days now. Reading your comment, my penny dropped. You've made my day sir! Will stick to BSRR/BRR registers for bit operations on I/O now. \$\endgroup\$
    – BertVano
    Jul 9, 2020 at 14:51
  • \$\begingroup\$ @BertVano: Even if you wants to write multiple bits, BSRR is the way to go unless you want to write all of them. If e.g. you want to write bits 4-6, and newValue has no bits set other than those, thePort->BSRR = 0x00700000 | newValue;.will atomically update those bits without affecting any others. I wish chip vendors would settle on ways of supporting atomic bit manipulations of I/O registers that are shared among different functions, since providing such support globally at the hardware level costs very little. Instead of having each register latch the state of the input when... \$\endgroup\$
    – supercat
    Jul 9, 2020 at 15:50
  • \$\begingroup\$ ...a write occurs and the address selected, have one address bit control whether the write should affect bits that are presently set, and another to control whether it should affect bits that are presently clear (if both are set, perform the write unconditionally). For some purposes, a BSSR-style design is better, but that's effectively limited to 16-bit registers. For many purposes, performing all write-one operations atomically and all write-zero operations atomically would suffice, and would be easy to handle. \$\endgroup\$
    – supercat
    Jul 9, 2020 at 16:07

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.