Skip to main content
JReed.1856
Associate II
April 13, 2022
Solved

Quickest way to set multiple port pins

  • April 13, 2022
  • 18 replies
  • 6504 views

Hi,

I am trying to connect to a 16 bit parallel display interface. To write new data, I have to use pins on five different ports (GPIOs A, B, C, D, E). Right now I am setting these pins like this:

PORT_D15->BSRR = (PIN_D15 << 16) | (((data >> 15) & 0x01) * PIN_D15) ;

repeat for all other 15 pins.

This is causing the display to update slowly, you can see the screen 'rolling in' from the top.

On a previous controller all pins were in one port, and one access to

PORT_DISPLAY->ODR

was enough to set all pins.

Is there a quicker way to set all those port pins?

This topic has been closed for replies.
Best answer by TDK

Seems like you could do a little better with:

PORT_D15->BSRR = (PIN_D15 << 16) >> (16 * ((data >> 15) & 0x01));

or possibly

PORT_D15->BSRR = (data & (1 << 15)) ? PIN_D15 : PIN_D15 << 16;

Apart from that, agree with JW's assessment.

18 replies

S.Ma
Principal
April 13, 2022

Which part number?

The core or dma can write 8 or 16 bit in 1 cycle, so group by 8 pins say PA8..15 and PC0..7. The fastest is one full port dedicated to your display. Otherwise, if available, check FMC

JReed.1856
Associate II
April 13, 2022

STM32F405

unfortunately due to hardware restrictions I can't change the pins or group the pins anymore, otherwise I would just use the approoach of one ODR access.

waclawek.jan
Super User
April 13, 2022

This is the exact case where you can't fix in software what you've broken in hardware. There's no silver bullet, and the software ways to split/assemble bits is inherently extensive and slow.

But you can increase your pain until you give up, if you fancy that: play with compiler optimization, faster execution from RAM, go for asm, try partial table lookup.

Or, if the display controller supports it, you may try feeding it through 8-bit or even SPI.

JW

TDK
TDKBest answer
April 13, 2022

Seems like you could do a little better with:

PORT_D15->BSRR = (PIN_D15 << 16) >> (16 * ((data >> 15) & 0x01));

or possibly

PORT_D15->BSRR = (data & (1 << 15)) ? PIN_D15 : PIN_D15 << 16;

Apart from that, agree with JW's assessment.

"If you feel a post has answered your question, please click ""Accept as Solution""."
S.Ma
Principal
April 14, 2022

Maybe check the io lock register to directly write odr protecting the not related pins....

Piranha
Principal III
April 14, 2022

There is a faster, smaller and easier way:

PORT_D15->BSRR = (mask << 16ul) | data;

It works for multiple pins of a port at once. The set/reset logic is based on the following note in the reference manual:

"If both BSx and BRx are set, BSx has priority."

Other than that Jan's answer is the most relevant.

JReed.1856
Associate II
April 19, 2022

Could you elaborate on this? I assume the mask is contains the port pins, so if say GPIOA has Display pins on Pins 0, 1 and 3 the mask would look like this: 1011.

However if data contains bits for pins, that shouldn't be changed on this port because the port pins have other functions apart from the display they would be changed as well.

Additionally by shifting 16 left there are always 0s in the lower 16 bits, so data would never be set?

waclawek.jan
Super User
April 19, 2022

Piranha's point is, that you can always set all BRx bits corresponding to the display's pins, because BSx bits set by data take precedence.

As (mask << 16) is a constant expression, it's evaluated at compile time as constant and there is no need for the shift to be performed at runtime, reducing the code to loading the constant and OR with data.

JW

PS. Ah, now I see it... & instead of I, a typo, confusing indeed. Funny, knowing what the expression should do, I overlooked it, too...

Piranha
Principal III
April 19, 2022

Thanks, Jan. That indeed was a "typo" - initial version was a bit different, I edited it and left the wrong operator. Fixed it now.

By the way, the code will work even if mask is not a constant, but of course there will be an additional shift operation done at runtime.

JReed.1856
Associate II
April 19, 2022

But the way I understand this, I would have to AND data and mask as well, otherwise there would be pins set, that don't lead to the display at all. Am I misunderstanding?

Piranha
Principal III
April 19, 2022

If the data variable can have non-display bits set, then indeed you have to do it, but that's and additional operation. As you are asking for quickest way, it's better to ensure the data variable only uses the display related bits.

Actually the absolute quickest way is the one suggested by user ".". Lock the non-display pins at the port and just write data to ODR register. Sorry, this is also wrong because locking lock only the configuration registers, not data registers. Reference manual:

The frozen registers are GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR, GPIOx_PUPDR, GPIOx_AFRL and GPIOx_AFRH.

waclawek.jan
Super User
April 19, 2022

mask is a constant, so no need to AND it.

But yes, you are right, data would need to be masked with the same mask. Okay, so that's one more AND.

Still, the negation, masking AND and left-shift for the BRx portion would be spared down.

JW

JReed.1856
Associate II
April 19, 2022

I just tried to implement it and I think this approach overlooks the fact, that my pins are not in order.

So lets say, the used display pins on GPIOC are on PC2, PC7, PC12, however PC2 has Bit4, PC7 has Bit12 and PC12 has Bit9 of the parallel data to be written.

masking GPIOC->BSRR works for the higher reset pins, but when it comes to setting, I can't see a way to mask the data, so that the pins are set correctly right away.

To me it seems there still has to be the assignment of pins like in TDKs answer.

So this would only work if PC2 was databit2, PC7 data bit 7 and PC12 bit 12.