Picoboard, Arduino, Gpio / Port direkt per Register-Zugriff steuern?

OP #7401031
Lesenswert?

Hallo,

ich verwende das picoboard RP2040 mit arduino framework. Weiss jemand ob man ähnlich wie bei Atmega, Attiny Serie den GPIO Port/Pin direkt per Register ansprechen kann wie z.B.?

1
    DDRD = 0xFF;
2
    PORTD |= (1 << 1);

statt via pinMode(), digitalWrite()...?

Danke

#7401137
Lesenswert?

Epi K. schrieb:

und wie findet man den Layer wo diese Register benannt sind? Und sonst, wie sieht das aus? Irgendwie 0x0000xxx = 0x0000xxx;

Sorry das geht mir zu tief, bzw. weiss nicht wo anfangen... Beispiele wären cool, da kommt man dann schnell rein.

Du suchst im Datenblatt nach SIO (Single-cycle IO) bzw. Atomic Register Access. Umschalten eines Portpins dauert bei 125MHz dann lediglich 8ns.

#7401171
Lesenswert?

Epi K. schrieb:

geht das so?:

0x40014000 = 0x1000 //bit13 setzen,

Nö. Es gibt sicher Headerfiles im Compiler vom Arduino, wo die Register per Namen ansprechbar sind.

Dann ist GPIO0 als Ausgang definiert :-D ?

Wozu glaubst du, das tun zu müssen? Gerade auf dem Raspberry PI Pico ist das setzen der IOs so schnell, da kann man in den allermeisten Fällen das einfache digitalWrite() bzw. pinMode() nehmen. Das dauert vermutlich weniger als 1 us.

#7401213
Lesenswert?

Epi K. schrieb:

Weiss jemand ob man ähnlich wie bei Atmega, Attiny Serie den GPIO Port/Pin direkt per Register ansprechen kann

Kannst ja Mal in das RP2040 SDK reinschauen wie die das machen. Auch wenn du es nicht verwenden möchtest.

https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#hardware_gpio

https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_gpio/gpio.c

https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_gpio/include/hardware/gpio.h

Da siehst du dass das gpio_put() auf ein gpio_set_mask() oder gpio_clr_mask() rausläuft. Was wiederum ein sio_hw->gpio_set = mask; bzw sio_hw->gpio_clr = mask; macht.

#7401224
Lesenswert?

Epi K. schrieb:

geht das so?:

0x40014000 = 0x1000 //bit13 setzen,

Dann ist GPIO0 als Ausgang definiert :-D ?

Nein.

So aber vielleicht: *((unsigned int *)0x40014000) = 0x1000;

Wenn da wirklich ein Register liegt die auch mit 32 bit beschreibbar ist und man keine Speicher oder Register Protektion aktiv hat (wenn es auf dem Ding sowas gibt) dann müsste das klappen.

#7401298
Lesenswert?

Sooo, mal wieder wurden die urban legends widerlegt. Siehe Anhang. Als zufälliger Besitzer eines RP2040 hab ich einfach mal gemessen. Pin wackeln in Endlosschleife. Laut Arduino-IDE läuft die CPU mit 133 MHz.

digitalWrite() ~ 800kHz Register set/clr ~33MHz(!) Register togl ~ 22MHz(!)

Sollte reichen. Da kommt mein olles DS1052 schon an seine Grenzen!

#7401308
Lesenswert?

Falk B. schrieb:

digitalWrite() ~ 800kHz Register set/clr ~33MHz(!) Register togl ~ 22MHz(!)

Mach das mal mit Assembler. Da ist noch ne gewaltige Menge Luft nach oben. In einer Endlosschleife addieren sich die Zeit des str[hb] Befehls auf die SIO (XOR) und der branch Befehl. Wenn du die Flash-delays raus haben möchtest, dann noch im RAM laufen lassen. Da war selbst mein 50MHz anal. Philips überfordert. Konnte nur im time x10 etwas sehen.

#7401310
Lesenswert?

Norbert schrieb:

digitalWrite() ~ 800kHz Register set/clr ~33MHz(!) Register togl ~ 22MHz(!)

Mach das mal mit Assembler. Da ist noch ne gewaltige Menge Luft nach oben.

Nö. Die 2. Version braucht 4 (VIER) CPU-Takte für die gesamte Schleife!

In einer Endlosschleife addieren sich die Zeit des str[hb] Befehls auf die SIO (XOR) und der branch Befehl. Wenn du die Flash-delays raus haben möchtest, dann noch im RAM laufen lassen. Da war selbst mein 50MHz anal. Philips überfordert.

Genau, anal. Voll für den Arsch! ;-)

#7401315
Lesenswert?

Falk B. schrieb:

Nö. Die 2. Version braucht 4 (VIER) CPU-Takte für die gesamte Schleife!

33% zu viel! Laut Datasheet braucht der str[bh] Befehl nur einen Takt für SIO Operationen und der unconditional branch nur zwei. Macht Drei. Also 41 2/3 MHz. (bei 125)

Wenn man den branch mal vergisst, im Sinne eines Loop unroll, dann kann man also mit einem Taktzyklus toggeln. Ergo 8ns.

Ps. Man sieht's auf dem Scope, man kann's messen, sieht nur nicht mehr so schön aus. ;-)

#7401323
Lesenswert?

Epi K. schrieb:

Weiss jemand ob man ähnlich wie bei Atmega, Attiny Serie den GPIO Port/Pin direkt per Register ansprechen kann wie z.B.?

Ich weiß es ;-) Zunächst müssen einige Initialisierungen passieren, ob Eingang oder Ausgang, Pullup oder Pulldown, ggf. Stromstärke oder Schmitttrigger. Dafür kannst Du wohl die Arduino-Befehle verwenden.

Um sehr schnell einen Ausgang zu setzen oder zu löschen, gibt es spezielle Adressen, die ein atomares SET, CLEAR oder XOR durchführen. Diesen Adressen haben einen Offset zur Basisadresse und man findet sie beispielsweise in RP2040.h. Im Datenblatt findet sich eine kurze Beschreibung auf Seite 18, Kapitel 2.1.2 Beispiel-Code hätte ich auch zu bieten, wenn es notwendig sein sollte.

#7401328
Lesenswert?

J. S. schrieb:

ziemlich sinnfrei diese ständigen Pintoggle Rekorddiskussionen. Der RP2040 hat die PIO, für schnelle Spezialaufgaben macht es mehr Sinn sich damit zu beschäfftigen.

Nein, finde ich gar nicht. Hier geht es einzig und allein darum, falsche von richtigen Informationen zu trennen.

Und während ich ausgesprochen gerne die PIO Statemachines nutze, alles kann man mit den zwei mal 32 Instruktionen auch nicht erledigen.

Beitrag #7401336 wurde von einem Moderator gelöscht.
#7401407
Lesenswert?

Kleine Ergänzung. Die Funktionen aus der Doku des RP2040

1
static inline bool gpio_get(uint gpio);
2
static inline void gpio_set_mask(uint32_t mask);
3
static inline void gpio_clr_mask(uint32_t mask);
4
static inline void gpio_put(uint gpio, bool value);

gibt es schon in den Untiefen der Includes im gcc für den RP2040. Kann man ohne zusätzliche #include nutzen. Mit konstanten Argumenten wird das auch so weit optimiert, daß der einzige Registerzugriff übrig bleibt, die Schleife ist so schnell wie der Test 2 mit den direktem Registerzugriff!

1
#if TEST_NR == 4
2
  // toggle test, direct register access via non-Arduino function
3
    gpio_put(TEST_LED, HIGH);
4
    gpio_put(TEST_LED, LOW);
5
#endif

Wenn man mit variablen Argumenten arbeitet, was ich hier mal mit einer volatile Varible erzwungen habe, wird es langsamer, aber immer noch SEHR flott! Umschaltfrequenz ~11MHz, d.h. ein Funktionsaufruf dauert ca. 40ns!

1
volatile uint pin=TEST_LED;
2

3
...
4

5
#if TEST_NR == 5
6
  // toggle test, direct register access via non-Arduino function
7
    gpio_put(pin, HIGH);
8
    gpio_put(pin, LOW);
9
#endif
OP #7401556
Lesenswert?

Falk B. schrieb:

Epi K. schrieb:

Sagt wer? Du?

ja, hatte ich mit gpio_put() getestet... irgendwie sehr lahm, aber wohl dem MUX geschuldet..

Wer viel mißt, mißt Mist.

Ich hätte erwähnen sollen, dass es mir darum geht mehrere Pins gleichzeitig zu toggeln (oder min. 10pins). Mit gpio_put_all habe ich nur paar 100KHz erreicht. Messung kann ich ggf. am Nachmittag nachreichen.

1
void loop(){
2
        gpio_put_all(0xffffffff);
3
        gpio_put_all(0x00000000);
4
}
#7401608
Lesenswert?

Epi K. schrieb:

Ich hätte erwähnen sollen, dass es mir darum geht mehrere Pins gleichzeitig zu toggeln (oder min. 10pins).

1
#define BIT(x) (1<<x)
2
// mehrere Ausgaenge aktivieren
3
  SIO->GPIO_OE_SET =  BIT(7) | BIT(8) | BIT(9) | BIT(10);
4
// oder eben einzeln
5
  SIO->GPIO_OE_SET =  BIT(1);
6
  SIO->GPIO_OUT_CLR = BIT(1);  // LCD_RS auf 0
7
  kurz_warten();
8
  if((SIO->GPIO_IN & BIT(2))) return(0); // LCD_RW ist nicht verbunden
9
  SIO->GPIO_OE_CLR =  BIT(1);  // LCD_RS passiv
10
  SIO->GPIO_OE_SET =  BIT(2);
11
  SIO->GPIO_OUT_CLR = BIT(1);  // LCD_RS auf 0
12
  kurz_warten();
13
  if((SIO->GPIO_IN & BIT(2))) return(0); // LCD_RW ist nicht verbunden

Ein Codeschnipsel für direkte Zugriffe.

#7401657
Lesenswert?

Epi K. schrieb:

Mit gpio_put_all habe ich nur paar 100KHz erreicht.

Kann ich nicht bestätigen. Bei mir sind es mit deinem Code in einer while(true) um die 13.8MHz bei Debug und ca. 31.25MHz mit Release (siehe Anhang).

Verwendeter Code:

1
gpio_init_mask(0xffffffff);
2
gpio_set_dir_all_bits(0xffffffff);
3
gpio_set_drive_strength(10, GPIO_DRIVE_STRENGTH_12MA); //Max Strom fuer Messung
4
while(true)
5
{
6
  gpio_put_all(0xffffffff);
7
  gpio_put_all(0x00000000);
8
}

Geb dir mal den CLK_SYS an einem Pin aus. Hier ein Beispiel am PIN21 (Achtung, da geht nicht jeder Pin). Geteilt durch 10.0f damit man es besser messen kann.

1
clock_gpio_init(21,CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLK_SYS,10.0f);

Bei mir messe ich dann 12.5MHz, also 125MHz als Sys-Clock. Was anscheinend Default ist. Die Zeiten vom Anhang werden also noch leicht schneller sein wenn man auf die erlaubten 133MHz erhöht.

Und wie schon oben gesagt: Soll es noch schneller sein nimm eine PIO.

Angehängte Dateien:
#7401672
Lesenswert?

Das hier duerfte so ziemlich die Minimalversion sein:

https://github.com/dwelch67/raspberrypi-pico/tree/main/blinker00

Und ja, es ist natuerlich schon schoener wenn man sich ein Headerfile mit den Registern besorgt. .-)

Was leider fuer Anfaenger etwas verwirrend ist das ist dieser zweistufige Bootprozess mit Pruefsumme.

Deshalb schaut euch das was dwelch67 da gemacht hat genau an. Das erleichtert den Einstieg ernorm.

Vanyo

OP #7402139
Lesenswert?

N. M. schrieb:

Epi K. schrieb:

Mit gpio_put_all habe ich nur paar 100KHz erreicht.

Kann ich nicht bestätigen. Bei mir sind es mit deinem Code in einer while(true) um die 13.8MHz bei Debug und ca. 31.25MHz mit Release (siehe Anhang).

Vielen Dank für diese Hinweise. Ich verwende platformIO / Arduino mit folgendem Setup, und im Anhang die Messung. Das Signal ist komisch, aber immerhin jetzt mehr als paar 100kHz, d.h.:

gpio_put_all: unsymetrisches Signal ca. 7.3MHz

sio_hw->gpio_set u. clr: unsymetrisches Signal ca. 6.9Mhz

sio_hw->gpio_togl: symetrisches Signal ca. 3.6MHz

1
[env:pico]
2
platform = raspberrypi
3
board = pico
4
framework = arduino
5
upload_protocol = picotool
6
board_build.f_cpu = 133000000L

Code:

1
uint32_t mask = 1L<<21;
2

3
void setup(){
4
  gpio_init_mask(0xffffffff);
5
  gpio_set_dir_all_bits(0xffffffff);
6
  gpio_set_drive_strength(21, GPIO_DRIVE_STRENGTH_12MA); //Max Strom fuer 
7
}
8
void loop() {
9
        gpio_put_all(0xffffffff);
10
        gpio_put_all(0x00000000);
11

12
      //sio_hw->gpio_set = mask;
13
      //sio_hw->gpio_clr = mask;
14
      //sio_hw->gpio_togl = mask;
15
}

N. M. schrieb:

clock_gpio_init(21,CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLK_SYS,10.0f);

Gibt es nicht bei mir.

Angehängte Dateien:
#7402182
Lesenswert?

Hier mal eine gescheite HF-Messung des IO-Pins. Gemessen mit einem nagelneuen LeCroy Wavesurfer 4054HD (500MHz, 5 Gs/s, 12Bit) und einem selbstgebauten Z0-Tastkopf mit 1k Eingangswiderstand. Die IOs sind RATTENSCHNELL! Dargestellte Anstiegszeit ca. 1ns, davon sind aber ca. 0,7ns schon das Oszi! (toszi~0,35/f3dB).

tr = Wurzel (tg^2 - toszi^2) = Wurzel (1ns^2-0,7ns^2) = 0,7ns

Alter Schalter!

Die beiden ersten Messungen wurden mit interner Terminierung mit 50 Ohm gemacht, da hat man schon eine sehr gute Signalqualität. Die dritte Messung mit externer Terminierung an einem T-Stück und Oszi auf 1M Eingangswiderstand. Das ist DEUTLICH schlechter! Ob das am schon etwas älteren, leicht oxidierten T-Stück oder dem Oszi liegt, ist unklar.

Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren