stm32duino / Arduino_Core_STM32

STM32 core support for Arduino
https://github.com/stm32duino/Arduino_Core_STM32/wiki
Other
2.76k stars 963 forks source link

API for operating multiple GPIOs in parallel #1005

Closed nopnop2002 closed 4 years ago

nopnop2002 commented 4 years ago

Feature request:

Arduino for ATmega has an API for operating multiple GPIOs collectively on a per port basis.

By using this, for example, TFT of 8-bit parallel I/F can be handled at high speed.

Arduino_Core_STM32 does not have such an API.

We hope that such an API can be used in the future.

My Image:

pinMode(PB0, OUTPUT);
pinMode(PB1, OUTPUT);
pinMode(PB2, OUTPUT);
pinMode(PB3, OUTPUT);
pinMode(PB4, OUTPUT);
pinMode(PB5, OUTPUT);
pinMode(PB6, OUTPUT);
pinMode(PB7, OUTPUT);
digitalWrite(PB0, 1);
digitalWrite(PB1, 1);
digitalWrite(PB2, 0);
digitalWrite(PB3, 0);
digitalWrite(PB4, 0);
digitalWrite(PB5, 0);
digitalWrite(PB6, 1);
digitalWrite(PB7, 1);

to

pinPortMode(PBLOW, OUTPUT);  // Mode set from PB0 to PB7
digitalPortWrite(PBLOW, 0xC3);  // Output from PB0 to PB7

I look forward to your consideration.

uzi18 commented 4 years ago

stm32 have got more pins on one port than atmega

fpistm commented 4 years ago

Where this API is described? This is not an official one? You can use LL or direct access register to do this.

nopnop2002 commented 4 years ago

What is LL??

Actual Ardino Code:

void setup() {
   DDRD = DDRD | B11111111; // output pins D0-D7
   PORTD = B10101000; // Digital pins D7, D5, D3 set to HIGH
}

void loop() {
  // put your main code here, to run repeatedly:

}

This allows for fast parallel IO.

But Arduino_Core_STM32 is not support this.

It is possible to operate multiple GPIOs by manipulating the registers directly, but it is not general purpose.

STM32F103: GPIOx_CRL,GPIOx_CRH,GPIOx_ODR

STM32F303: GPIOx_MODER,GPIOx_OTYPER,GPIOx_ODR

I'm happy if Arduino_Core_STM32 supports this (or similar feature)

fpistm commented 4 years ago

What is LL??

This is:

The Low Layer APIs (LL) offering a fast light-weight expert-oriented layer which is closer to the hardware than the HAL. The LL APIs are available only for a set of peripherals.

About:

Actual Ardino Code: ...

In fact you want this: https://www.arduino.cc/en/Reference/PortManipulation

This is purely related to some AVR.

The chips used on the Arduino board (the ATmega8 and ATmega168) have three ports:

B (digital pin 8 to 13) C (analog input pins) D (digital pins 0 to 7)

STM32 has several port A up to K and 16 pins for each and this not inline with the pin numbering.

You can use the CMSIS definition to achieve this easily. Several libraries already do this or using those functions: https://github.com/stm32duino/Arduino_Core_STM32/blob/a9fdc93fc8b2d5739f0d2100d81a83789dd0bbd0/cores/arduino/pins_arduino.h#L293-L318

Or you can use the LL GPIO by including "stm32yyxx_ll_gpio.h" and using all LLGPIO* functions. Refers to description of STM32xx HAL and LL drivers, example for F4: https://www.st.com/resource/en/user_manual/dm00105879-description-of-stm32f4-hal-and-ll-drivers-stmicroelectronics.pdf

Note that Teensy implement an AVR emulation: https://github.com/PaulStoffregen/cores/blob/f9a9f867c565b0740cd9a36c10bc2e6a6638fa00/teensy4/avr_emulation.h

It can be done like this but this is confusing because for PORTD this would not means GPIO PORT D but all digital pins 0 to 7 which can be on different GPIO port...

stas2z commented 4 years ago

LL is not that scary for GPIO lets imagine we need to set pins on PD0/PD1/PD2

__HAL_RCC_GPIOD_CLK_ENABLE(); // enable GPIO port D clock
/* set pin mode */
LL_GPIO_SetPinMode(GPIOD, LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(GPIOD, LL_GPIO_PIN_1, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(GPIOD, LL_GPIO_PIN_2, LL_GPIO_MODE_OUTPUT);

/* output type, use LL_GPIO_OUTPUT_OPENDRAIN for open drain active low pins */
LL_GPIO_SetPinOutputType(GPIOD,  LL_GPIO_PIN_0 |  LL_GPIO_PIN_1 |  LL_GPIO_PIN_2, LL_GPIO_OUTPUT_PUSHPULL);
/* no pullup or pulldown for output pins */
LL_GPIO_SetPinPull(GPIOD, LL_GPIO_PIN_0 |  LL_GPIO_PIN_1 |  LL_GPIO_PIN_2, LL_GPIO_PULL_NO);
/* set gpio speed */
LL_GPIO_SetPinSpeed(GPIOD, LL_GPIO_PIN_0 |  LL_GPIO_PIN_1 |  LL_GPIO_PIN_2, LL_GPIO_SPEED_FREQ_HIGH);

/* and finally */
LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_0 |  LL_GPIO_PIN_1 |  LL_GPIO_PIN_2);
/* or */
LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_0 |  LL_GPIO_PIN_1 |  LL_GPIO_PIN_2);
/* or */
LL_GPIO_TogglePin(GPIOD, LL_GPIO_PIN_0 |  LL_GPIO_PIN_1 |  LL_GPIO_PIN_2);
nopnop2002 commented 4 years ago

@stas2z It was LL_GPIO_TogglePin instead of LL_GPIO_ToggleOutputPin. This code work fine with STM32F103/F303/F401/F405.

void setup() {
  pinMode(PA0, OUTPUT);
  pinMode(PA1, OUTPUT);
  pinMode(PA2, OUTPUT);
  pinMode(PA3, OUTPUT);
  pinMode(PA4, OUTPUT);
  LL_GPIO_WriteOutputPort(GPIOA, 0);
}

void loop(){
  LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_0 |  LL_GPIO_PIN_1 |  LL_GPIO_PIN_2 |  LL_GPIO_PIN_3 |  LL_GPIO_PIN_4);
  delay(1000);
}

@fpistm

I found the following document, but this document was not published by STM.

http://www.disca.upv.es/aperles/arm_cortex_m3/llibre/st/STM32F439xx_User_Manual/group__gpio__ll__ef__data__access.html

For many people, STM need to provide API documentation or sample code.

You may publish it here.

https://github.com/stm32duino/wiki/wiki

https://github.com/stm32duino/wiki/wiki/Examples

Thank you for your help.

nopnop2002 commented 4 years ago

I tried the effect of LL_GPIO_WriteOutputPort() on some board.

digitalWrite Code:

uint32_t Pins[] = {PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7};

void write8(uint8_t bytes) {
  for(uint8_t bits=0;bits<8;bits++) {
    uint8_t mask = 1<<bits;
#if 0
    Serial.print("mask=");
    Serial.print(mask,HEX);
    Serial.print(" ");
    Serial.println((bytes & mask), HEX);
#endif
    if ( (bytes & mask) != 0 ) {
      digitalWrite(Pins[bits], HIGH);
    } else {
      digitalWrite(Pins[bits], LOW);
    }
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(PA0, OUTPUT);
  pinMode(PA1, OUTPUT);
  pinMode(PA2, OUTPUT);
  pinMode(PA3, OUTPUT);
  pinMode(PA4, OUTPUT);
  pinMode(PA5, OUTPUT);
  pinMode(PA6, OUTPUT);
  pinMode(PA7, OUTPUT);
  write8(0x0);
}

void loop(){
  uint8_t bytes = 0;
  uint32_t smill = millis();
  for(uint32_t i=0;i<100000;i++) {
    write8(bytes);
    bytes++;
    //delay(100);
  }
  uint32_t emill = millis();
  Serial.print("Elasped=");
  Serial.println(emill - smill);
}

LL_GPIO_WriteOutputPort Code:

uint32_t Pins[] = {LL_GPIO_PIN_0, LL_GPIO_PIN_1, LL_GPIO_PIN_2, LL_GPIO_PIN_3, LL_GPIO_PIN_4, LL_GPIO_PIN_5, LL_GPIO_PIN_6, LL_GPIO_PIN_7};

void write8(uint8_t bytes) {
  uint32_t PinMask = 0;
  //PinMask = 0;
  for(uint8_t bits=0;bits<8;bits++) {
    uint8_t mask = 1<<bits;
#if 0
    Serial.print("mask=");
    Serial.print(mask,HEX);
    Serial.print(" ");
    Serial.println((bytes & mask), HEX);
#endif
    if ( (bytes & mask) != 0 ) PinMask = PinMask + Pins[bits];
  }
#if 0
  Serial.print("bytes=");
  Serial.print(bytes,HEX);
  Serial.print(" PinMask=");
  Serial.println(PinMask,HEX);
#endif
  LL_GPIO_WriteOutputPort(GPIOA, PinMask);
}

void setup() {
  Serial.begin(115200);
  pinMode(PA0, OUTPUT);
  pinMode(PA1, OUTPUT);
  pinMode(PA2, OUTPUT);
  pinMode(PA3, OUTPUT);
  pinMode(PA4, OUTPUT);
  pinMode(PA5, OUTPUT);
  pinMode(PA6, OUTPUT);
  pinMode(PA7, OUTPUT);
  write8(0x0);
}

void loop(){
  uint8_t bytes = 0;
  uint32_t smill = millis();
  for(uint32_t i=0;i<100000;i++) {
    write8(bytes);
    bytes++;
    //delay(100);
  }
  uint32_t emill = millis();
  Serial.print("Elasped=");
  Serial.println(emill - smill);
}
digitalWrite LL_GPIO_WriteOutputPort Faster
STM32F103 924 252 x3.6
STM32F303 855 218 x3.9
STM32F401 399 129 x3.1
STM32F405 205 67 x3.1

It is about three times faster. I'm very glad if i gets more fast.

reference: Output from D2 to D9 using ATMega328

digitalWrite PortManipulation
ATmega328 5204 264

But I am satisfied because it is faster than ATmega328

Thank you for your help.

stas2z commented 4 years ago

Oh, sorry for toggle mistake Im not sure you can make it faster than with LL, cuz LL are mostly wrappers around registers The only way to spend less time on it, to write your port with dma probably

nopnop2002 commented 4 years ago

@stas2z

Thanks for very useful information. I was able to get very useful information in 24 hours. I am very happy with this result.

@fpistm

Such useful information should be widely publicized.

stas2z commented 4 years ago

@fpistm

I found the following document, but this document was not published by STM.

http://www.disca.upv.es/aperles/arm_cortex_m3/llibre/st/STM32F439xx_User_Manual/group__gpio__ll__ef__data__access.html

For many people, STM need to provide API documentation or sample code.

it's provided, for example for F4 but im sure similar docs exist for other series https://www.st.com/resource/en/user_manual/dm00105879-description-of-stm32f4-hal-and-ll-drivers-stmicroelectronics.pdf

nopnop2002 commented 4 years ago

I found your post easier to understand than this document. https://www.st.com/resource/en/user_manual/dm00105879-description-of-stm32f4-hal-and-ll-drivers-stmicroelectronics.pdf

fpistm commented 4 years ago

Yes, all use manual are available on st.com: https://www.st.com/content/st_com/en/search.html#q=Descriptions%20hal%20and%20ll-t=resources-page=1

Do not hesitate to provide an example in STM32Exampes library or in the forum.

Note that all HAL and LL ca be used for for all purposes.

nopnop2002 commented 4 years ago

@stas2z @fpistm

I close this isuues.

Japan is the season for cherry blossoms. Thank you for your help.