cancel
Showing results for 
Search instead for 
Did you mean: 

Advice on STM32F103 GPIO port sharing

Grant Bt
Senior

First, the "atomic" port ODR issue when more than one function or task may use the same GPIO port. Is the best thing to do ALWAYS to use BRR and/or BSRR to change the ODR values? From what I've read we need to be wary of an interrupt or other task changing an ODR value behind our backs if we are using Read-Modify-Write methods. I don't forsee painting myself into this corner, but better safe than sorry I guess.

Second "atomic" problem, what do we do about changing port configuration? If we need to switch some pins from in to out, or out to in, do we then disable interrupts while doing so? I don't see another solution.

Finally, about this input-output port re-configuration. If I want to write to a device dangling off a port, I can see that I should select one of the push-pull modes and write the data (strobing the external part as needed). To change the port to now read from it, do I select Hi-Z and then set the ODR bits high? Then strobe/read? Is there an order of occurrence or other gotcha I should be on the lookout for? Obviously I want to avoid contention on the pins and also keep the speed up.

8 REPLIES 8
Grant Bt
Senior

I guess one other possibility for the bidirectional problem in paragraph three is to choose hi-z and use external pull-ups. Is this better than trying to "turn the port pins around" via changing CRL and/or CRH?

TDK
Guru

> Is the best thing to do ALWAYS to use BRR and/or BSRR to change the ODR values?

It's faster, and atomic, so yes there are only upsides. Maybe you could argue that it's more complicated to understand than changing the ODR value, but from a performance standpoint it's strictly better.

> Second "atomic" problem, what do we do about changing port configuration? If we need to switch some pins from in to out, or out to in, do we then disable interrupts while doing so?

Yes, if your main loop modifies registers and you interrupt also does, you'll need to disable interrupts in your main loop while you change them, unless you have another way of guaranteeing they're not both changing it at the same time.

Changing port configurations within an IRQ is not something that is commonly needed.

> Finally, about this input-output port re-configuration.

You can change between input and output without intermediate states by setting the CNFx and MODEx fields at the same time. They're in the same register.

If you need to change the ODR register to change from pullup to pulldown, I would do it after setting it to input mode, although an argument could be made either way. If you have pull enabled disabled, doesn't matter what ODR is.

When switching from input to output, set the value in ODR before you enable output mode to avoid the pin possibly toggling.

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

Only the ancient 'F1xx have this bizarre GPIO scheme where pullup is controlled by ODR. All other STM32 families have decoupled control of pullup/pulldown.

Open-drain for bidir is an option; as is always the case for open drain, you have to consider the RC constant of the pullup and any pin/track capacitive load, for the output to go from 0 to 1. Unfortunately, the "quasibidir" scheme of classic 8051 with its fast-charging strong-pullup-pulse method is probably not replicated on any other mcu.

For bidirectional parallel data you should consider using FSMC which is designed exactly for this purpose. FSMC is not present in the smaller mcu models.

JW

Grant Bt
Senior

Thanks guys! A comment and followup question.

I'm an idiot. I made some mistakes coding where hi-z was set and then the ODR was set to 0 or 1 for the pulldowns/pullups. In fact I should have chosen the (other) digital input mode for use with the pulldown/pullups (not hi-z). I have not had time to correct this yet, but I'm thinking it's going to make a difference LOL :) I will try my code as hi-z and see what the data rise-times are and compare with pull-up mode.

This is probably a topic of it's own, but regarding disabling/enabling interrupts: Keil has __enable_irq() and __disable_irq() in the event I want to protect code that messes with (shared) CRL or CRH. One bad thing that could happen is that I could (re-)enable the interrupts if they were initially already off. Is there some way to know the current setting of the I flag? So that I only re-enable the interrupts if they were initially on. I see this has a return value, but don't understand it:

static __inline__ unsigned int __attribute__((__always_inline__, __nodebug__))
__disable_irq(void) {
  unsigned int cpsr;
#if __ARM_ARCH >= 6
#if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M'
  __asm__ __volatile__("mrs %[cpsr], primask\n"
                       "cpsid i\n"
                       : [cpsr] "=r"(cpsr));
  return cpsr & 0x1;
#else /* !defined(__ARM_ARCH_PROFILE) || __ARM_ARCH_PROFILE != 'M' */
  __asm__ __volatile__("mrs %[cpsr], cpsr\n"
                       "cpsid i\n"
                       : [cpsr] "=r"(cpsr));
  return cpsr & 0x80;
#endif
#else /* __ARM_ARCH < 6 */
  unsigned int tmp;
  __asm__ __volatile__(
          "mrs	%[cpsr], CPSR\n"
          "bic	%[tmp], %[cpsr], #0x80\n"
          "msr	CPSR_c, %[tmp]\n"
          : [tmp]"=r"(tmp), [cpsr]"=r"(cpsr));
  return cpsr & 0x80;
#endif
}

Finally, I see comments about how the Keil functions for interrupt control don't actually work since there is User Mode I may find myself operating in.

Thanks,

GB

> If you have pull enabled, doesn't matter what ODR is.

Hi. Is this a typo? Or am I blind here? If you have pull (up/down) enabled, doesn't ODR set the pullup(1) or pulldown(0)?

Yes, it should read “disabled�?.
If you feel a post has answered your question, please click "Accept as Solution".

OK, cool, thanks. I thought I was losing my marble.

Found the interrupts info. Or at least part of it:

https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_chr1359124995648.htm

void foo(void)
{
int was_masked = __disable_irq();
 
/* ... */
 
if(!was_masked)
   __enable_irq();
}