Need a define for "digitalPinToPort" for Giga

digitalPinToPort is not defined for the Giga.

on an STM32, it looks like:
#define digitalPinToPort(p) (get_GPIO_Port(STM_PORT(digitalPinToPinName(p))))
but defining it as such points to other undefined defines...

on a AVR Mega, it looks like:
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
but this will not work, because it is not really an AVR...

so what should digitalPinToPort look like for the Giga?

Thanks in advance for any help :slight_smile:

It may depend on what you mean by ToPort. Or more specifically what you wish to do with it.

That is, the GPIO ports, contain multiple registers, like:
GPIOx_IDR = Port Input register
GPIOx_ODR = Port Output register

Note: I typically avoid changing output register directly unless for known specific things, as if not careful changes are typically not atomic and you run risks if other code like Interrupts or other threads run, in between the time you fetch the register and the time you store out your changes, as something else may have changed the state of other pins on the part, which you will now clobber....

So I use the GPIOx_BSRR register where the lower 16 bits (any bits that are set to 1, will set the corresponding bit in the ODR to 1 and the upper 16 bits (any bit that is a 1 will clear the corresponding bit within the ODR register.

I currently have code in couple of places that uses a static table like:

  static  GPIO_TypeDef * const port_table[] = { GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK };
...
  // chip select pin
  PinName pin_name = g_APinDescription[_cs].name;
  GPIO_TypeDef  *  port = port_table[pin_name >> 4];
  _csBSRR = (__IO uint32_t *)(&port->BSRR);
  _cspinmask = 1 << (pin_name & 0xf);
  ..
  //digitalWrite(_cs, LOW);
  *_csBSRR = (uint32_t)(_cspinmask << 16);  // reset
..
    //digitalWrite(_cs, HIGH);
    *_csBSRR = (uint32_t)(_cspinmask);  // set

Note: I believe I can get rid of the static table by computing the address
Maybe something like:

GPIO_TypeDef * port = ((GPIO_TypeDef *)(GPIOA_BASE + (GPIOB_BASE - GPIOA_BASE) * (pin_name >> 4));

Note: Typed this on fly so, might be typos...

#define portOutputRegister(P)       (&(P->ODR))
#define portInputRegister(P)        (&(P->IDR))
//other code here
GPIO_TypeDef port = digitalPinToPort(pin);
    if (input)
      result.inout = portInputRegister(port);
    else
      result.inout = portOutputRegister(port);

so in effect, i am just looking to feed the result of digitalPinToPort into:

#define portOutputRegister(P)       (&(P->ODR))
#define portInputRegister(P)        (&(P->IDR))

your code seems to work, after a brief edit, thanks :slight_smile:
well, it passes compile. now i just have to work through the rest of the code.

I don't suppose you have heard of a shadowPORTn (where n=A to ...)?
i seem to be stuck looking through the native and core code looking for references...

Sorry no have no idea what that is...

Side note, here is my quick and dirty version of calls like: digitalWriteFast...
UNOR4-stuff/libraries/GIGA_digitalWriteFast/GIGA_digitalWriteFast.h at main · KurtE/UNOR4-stuff (github.com)

Hi All,

Here is a Program I just Wrote which Directly Accesses the Port of your choice.

Let me know if you have any Questions.

Thanks Paul

//********************
//********************
//********************
// This Program Outputs a Byte to the "GIOJ" Port Pins: D38 (PJ7), D37 (PJ6), D35 (PJ5), D33 (PJ4), D31 (PJ3), D29 (PJ2), D27 (PJ1), D25 (PJ0) 
//********************
//********************
//********************

//Set Byte_Test to Word for "Serial.println"
word Byte_Test;

void setup() {

  Serial.begin(9600);

  Serial.println("Encoder Test --> Giga Direct Port Manipulation!!!");

//********************
//********************
//********************

//Set Desired Byte Pins to OUTPUT
  pinMode(25, OUTPUT);
  pinMode(27, OUTPUT);
  pinMode(29, OUTPUT);
  pinMode(31, OUTPUT);
  pinMode(33, OUTPUT);
  pinMode(35, OUTPUT);
  pinMode(37, OUTPUT);
  pinMode(38, OUTPUT);

}

void loop() {
 //Serial.println("Encoder Test --> Giga Direct Port Manipulation!!!"); 

//********************
//********************
//********************

//Copy 'n' Paste Code that Actually Works!!!!
//GPIOJ->ODR = (0b00000001); // Turns on: D25 (PJ0)
//GPIOJ->ODR = (0b00000010); // Turns on: D27 (PJ1)
//GPIOJ->ODR = (0b00000011); // Turns on: D27 (PJ1), D25 (PJ0)
//GPIOJ->ODR = (0b00000100); // Turns on: D29 (PJ2)
//GPIOJ->ODR = (0b00000101); // Turns on: D29 (PJ2), D25 (PJ0)
//GPIOJ->ODR = (0b00000110); // Turns on: D29 (PJ2), D27 (PJ1)
//GPIOJ->ODR = (0b00000111); // Turns on: D29 (PJ2), D27 (PJ1), D25 (PJ0) 
//GPIOJ->ODR = (0b00001000); // Turns on: D31 (PJ3)
//GPIOJ->ODR = (0b00001001); // Turns on: D31 (PJ3), D25 (PJ0)
//GPIOJ->ODR = (0b00001010); // Turns on: D31 (PJ3), D27 (PJ1)
//GPIOJ->ODR = (0b00001011); // Turns on: D31 (PJ3), D27 (PJ1), D25 (PJ0)
//GPIOJ->ODR = (0b00001100); // Turns on: D31 (PJ3), D29 (PJ2)
//GPIOJ->ODR = (0b00001101); // Turns on: D31 (PJ3), D29 (PJ2), D25 (PJ0)
//GPIOJ->ODR = (0b00001110); // Turns on: D31 (PJ3), D29 (PJ2), D27 (PJ1)
//GPIOJ->ODR = (0b00001111); // Turns on: D31 (PJ3), D29 (PJ2), D27 (PJ1), D25 (PJ0)
//   *
//   *
//   *
//GPIOJ->ODR = (0b11111111); // Turns on: D38 (PJ7), D37 (PJ6), D35 (PJ5), D33 (PJ4), D31 (PJ3), D29 (PJ2), D27 (PJ1), D25 (PJ0)

//********************
//********************
//********************

//Set GIPO "J" Port to Desired Byte
//This Turns On / Off Desired Digital Ports
GPIOJ->ODR = (0b00000111); // Turns on: D29 (PJ2), D27 (PJ1), D25 (PJ0) 

//Set "Byte_Test" to Desired Byte
Byte_Test =  (GPIOJ->ODR);

//Prints "Byte_Test" to "Serial Monitor" in HEX      
Serial.println( Byte_Test, HEX );

}


Great solution! I was curious what speeds you get for such a pin turning on and off? I'm guessing this isn't capable of 480 MHz, assuming it takes a few clock cycles to interpret and then tell the pin what to do. I'd be interested in a bit of clarification as to how exactly this works.

If you have not already done so, for things like this it helps to download the reference
manual for the processor. In the version I downloaded rm0399 - STM32H745/755 and STM32H747/757 advanced Arm®-based 32-bit MCUs, GPIO is in chapter 12
If you go to thei GIGA documentation page, you can see the full pinout.
ABX00063-full-pinout.pdf

So for the pins mentioned above, you see.


So you see that pin 25 maps to hardware pin PJ0

//GPIOJ->ODR = (0b00000001); // Turns on: D25 (PJ0)

So the PJ says port J. One of the hardware include files, on my install located at:
<Arduino15_locatlion>\Arduino15\packages\arduino\hardware\mbed_giga\4.1.5\cores\arduino\mbed\targets\TARGET_STM\TARGET_STM32H7\STM32Cube_FW\CMSIS\stm32h747xx.h
defines GPIOJ to point to the GPIO register structure.

typedef struct
{
  __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
  __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
  __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
  __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
  __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
  __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
  __IO uint32_t BSRR;     /*!< GPIO port bit set/reset,               Address offset: 0x18      */
  __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
  __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
} GPIO_TypeDef;

So, they are setting the ODR register to the value 1, which turns on D25.
With the Side effect that it also turns off all of the other IO pins on port J that may have been on. like at least (27, 29, 31, 33, 35, ...)

Which is why I prefer to use the BSRR register. to turn on pin D25:
GPIOJ->BSRR = (1 << 0);

Only the IO pins whose bit is set in the lower 16 bits will be turned high,
Only the IO pins whose bit is set in the upper 16 bits will be turned low.
GPIOJ->BSRR = (1 << (0 + 16));

Hope that makes sense.
Note, I do similar stuff in my digitalWriteFast/digitalToggleFast/digitalReadFast as this hard coding, but instead have it look up the mapping from the pin number to how
the code has pin name, which encodes the port number and bit within it, which I then
use to compute register address. If you use it with constants, it in theory should be
optimized back down to more or less the same one instuction.

Thank you for the information.
I ended up testing both methods and I now see how
GPIOJ->ODR = (0b00000001);
is functionally the same in terms of speed as digitalWriteFast(PJ_0, HIGH); running BSRR and they both take 6 clock cycles (~25 ns) to execute.
I also ended up testing
digitalToggleFast(PJ_0);
which takes closer to 15 clock cycles (~62.5 ns) which I am assuming is due to the need for both reading the current state and writing the new state. This was confirmed since the execution time is within one clock cycle of digitalReadFast(PJ_0); digitalWriteFast(PJ_0, HIGH);.

Looks like I have some assembly code to find and look through for a clock cycle level understanding.