Parallel GPIO direct port write access for Photon


#1

Thought I’d ask before I started on the STM32 datasheet, I’ll get to it eventually.

I am looking to write a byte directly to the digital port. Example:
byte B10110101;
PortA |= (byte & 0xE0); // using a bitmask and the or operator
PortB |= (byte & 0x1F);

It has been said that D0 through D4 are on port B, and D5 through D7 are on port A on the Photon. How would I find that without tracing the PCB?

Promising and my first reads:

Spark Core related resources I found:


I’ll share if I find it first.


#2

The GPIOx_MODER sets the various pin functions, in my experience every analog pin is also a digital one, so I can use any combination of 8 pins on the photon to make this work. That extra few cycles actually matters in my case.

pinSetFast(D2 | D7);
Great for a single pin, just tried and it compiles but, D7 wins out regardless of the order, individually they work, also I wouldn’t want the overhead of 7 OR commands for my byte.


#3

The pin mapping can be found in the docs

And for direct port writes you can use this (off the top of my head, so I’d need to double check)

    GPIOA->ODR = 0xAAAA;    // directly setting the respective pins HIGH or LOW
    GPIOA->BSRRL = 0xAAAA;  // only setting the pins with a set bit to HIGH (counter intuitive tho')
    GPIOA->BSRRH = 0x5555;  // only resetting the pins with a set bit to LOW (counter intuitive tho')

There would also be an atomic instruction to have BSRRH & BSRRL set at once, but _IO uint32_t BSRR is not declared for some reason, but this workaround might work (not yet tested tho’)

   // this explanes why BSRRH & BSRRL are named counter intuitive in respect to their action.
   uint32_t* GPIOA_BSRR = (uint32_t*)&GPIOA->BSRRL; // use the address of uint16_t BSRRL as anchor for uint32_t BSRR
   *GPIOA_BSRR = 0x5555AAAA;  // the higher 16bit do the resetting of pins having a 1
                              // and the lower the setting, where setting wins over resetting if both bits are set.

#4

Thanks!

The article I read describes a single BSRR register 32 wide, the bottom 16 for setting a bit and the top 16 clear. The neopixel library has a reference to BSRRH and it works. I need to get my recently acquired programmer shield up and running.

STM32_Pin_Info* PIN_MAP2 = HAL_Pin_Map();
#define pinLO(_pin) (PIN_MAP2[_pin].gpio_peripheral->BSRRH = PIN_MAP2[_pin].gpio_pin)
#define pinHI(_pin) (PIN_MAP[_pin].gpio_peripheral->BSRR = PIN_MAP[_pin].gpio_pin)

I’ve looked at them plenty and I guess it’s a skill now to ignore ads or other superfluous information, but there is a STM32 Pin column in the documentation:
https://docs.particle.io/datasheets/photon-datasheet/#pin-out-diagrams

Where would I find firmware documentation. I’m going through the commented firmware files, just wondering if there is another resource I missed, open source FTW.


#5

Sorry, I meant to add that same link in my reply, but must have forgotten to :blush:
I didn’t mean to just drop that without the link

And my second code snippet was exactly addressing this

Since this code does not build for a Photon PIN_MAP[D0].gpio_peripheral->BSRR = 0xFFFF; because off

error: 'struct GPIO_TypeDef' has no member named 'BSRR'

But this would be a more concise yet slightly “cryptic” version of my proposed code above - and it builds :wink:

  *((uint32_t*)&GPIOA->BSRRL) = 0x5555AAAA;

#6

I’m working in the Particle online IDE. When I don’t select (star) any Photon device BSRRL and BSRRH give an error message.

gpioblink.ino:11:12: error: ‘struct GPIO_TypeDef’ has no member named 'BSRRL’
GPIOA->BSRRL = 0b0010000000000000; // HIGH

gpioblink.ino:16:12: error: ‘struct GPIO_TypeDef’ has no member named 'BSRRH’
GPIOA->BSRRH = 0b0010000000000000; // LOW

While BSRR doesn’t throw any error int that case.

However when I select my device (with the latest 0.6.3 firmware) the error is throw for BSRR and not for BSRRL/BSRRH.

gpioblink.ino:39:12: error: 'struct GPIO_TypeDef' has no member named 'BSRR'
GPIOA->BSRR = valueHigh + ((valueHigh ^ pinMaskD7) << 16);
          
gpioblink.ino:43:12: error: 'struct GPIO_TypeDef' has no member named 'BSRR'
GPIOA->BSRR = valueLow + ((valueLow ^ pinMaskD7) << 16);

Any idea’s if something has changed in the firmware/IDE related to this? Selecting latest 0.8.0RC1 didn’t work either to prevent an error for BSRR (although I couldn’t update my device firmware).

I’m curious why validating works different in both cases.


#7

If you haven’t selected any target device the Spark Core will be targeted by default and since this is running a STM32F0 controller that only has BSRR but not BSRRL/BSRRH you won’t get the same error you get with a STM32F2 based controller.

However nowadays this kind of bare metal access should not be used for such simple tasks. For that purpose a new set of functions has been introduced quite some time ago
https://docs.particle.io/reference/firmware/photon/#low-level-input-output

And if you need to go down to bare metal it’s solely the responsibility of the developer to ensure the targeted platform does actually provide the features before using them.


#8

Thanks for the explanation. I’m working on a small article regarding controlling pins with DMA, that’s why I wanted to show the register access. This whole post (including the missing BSRR workaround) was actually of great help for that, so thanks.


#9

Sounds interesting, please post a link here once complete.
From off the top of my head a year later, digitalWrite was ~60 cycles, pinfast was ~4 cycles and direct register was ~2-3 cycles. For most uses pinfast is awesome, if you were wanting to set 8 pins at once, cycles would add up, whereas direct you could do them all at once.


#10

Yup, hence the “simple tasks” bit.

For parallel access direct register access will still make sense, but that demands explicit insight into the hardware in use. Especially when Particle will run on other cores too.


#11

Hereby the link to my proof of concept code and article.