cancel
Showing results for 
Search instead for 
Did you mean: 

Quickest way to set multiple port pins

JReed.1856
Associate III

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?

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

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".

View solution in original post

18 REPLIES 18
S.Ma
Principal

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 III

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.

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
Guru

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

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

Piranha
Chief II

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.

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?

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...

JReed.1856
Associate III

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?