Forum: Mikrocontroller und Digitale Elektronik Picoboard, Arduino, Gpio / Port direkt per Register-Zugriff steuern?


von Epi K. (epi_k)


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

von Vanye R. (vanye_rijan)


Lesenswert?

Ja, kann man machen. Liess das Datenblatt da stehen die
Register drin.

Vanye

von Epi K. (epi_k)


Lesenswert?

Vanye R. schrieb:
> Ja, kann man machen. Liess das Datenblatt da stehen die
> Register drin.

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.

von Andras H. (kyrk)


Lesenswert?

Epi K. schrieb:
> Sorry das geht mir zu tief, bzw. weiss nicht wo anfangen...

Datenblatt finden und lesen.

https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf

Chapter 2.19 scheint wohl der GPIO Teil zu sein.

von Epi K. (epi_k)


Lesenswert?

geht das so?:

0x40014000 = 0x1000 //bit13 setzen,

Dann ist GPIO0 als Ausgang definiert :-D ?

von Norbert (der_norbert)


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.

von Falk B. (falk)


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.

von Falk B. (falk)


Lesenswert?

Andras H. schrieb:
> https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
>
> Chapter 2.19 scheint wohl der GPIO Teil zu sein.

Mein Gott, ab Seite 29 stehen FERTIGE C-Funktionen, die GENAU DAS tun 
was der OP will. Man muss sie nur kopieren! Ob das Gen-Z noch hinkriegt?

von Epi K. (epi_k)


Lesenswert?

Norbert schrieb:
> Umschalten eines Portpins dauert bei 125MHz dann lediglich 8ns.

ne glaube ich nicht, bei ca. 400..500KHz ist Schluss....

von N. M. (mani)


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.

: Bearbeitet durch User
von Andras H. (kyrk)


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.

von Falk B. (falk)


Lesenswert?

Epi K. schrieb:
>> Umschalten eines Portpins dauert bei 125MHz dann lediglich 8ns.
>
> ne glaube ich nicht, bei ca. 400..500KHz ist Schluss....

Sagt wer? Du?

von Epi K. (epi_k)


Lesenswert?

Falk B. schrieb:
> Sagt wer? Du?

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

von Norbert (der_norbert)


Lesenswert?

Epi K. schrieb:
> Norbert schrieb:
>> Umschalten eines Portpins dauert bei 125MHz dann lediglich 8ns.
>
> ne glaube ich nicht, bei ca. 400..500KHz ist Schluss....

Das Glauben überlassen wir doch besser den Klerikern.
Die haben Jahrhunderte lange Übung im alles Glauben und nichts Wissen.
Uns reicht da schon ein Blick auf's Oszilloskop. ;-)

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

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.

von Falk B. (falk)



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!

: Bearbeitet durch User
von Norbert (der_norbert)


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.

von Falk B. (falk)


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! ;-)

von Norbert (der_norbert)


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

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

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.

von Mi N. (msx)


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.

von Falk B. (falk)


Lesenswert?

Mi N. schrieb:
> Beispiel-Code hätte ich auch zu bieten, wenn es notwendig sein sollte.

Schon mal die anderen Beiträge gelesen? Du bist Stunden zu spät, 
außerdem ohne Substanz (Beispiele).

von Norbert (der_norbert)


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 vom Autor gelöscht.
von Falk B. (falk)


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

von Epi K. (epi_k)


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
}

von Falk B. (falk)


Lesenswert?

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

Tja, und schon wieder versagt Gen-Z kläglich. Schon mal über die 
Wirkungsweise der Funktionen gpio_set_mask(uint32_t mask) nachgedacht?

> Mit gpio_put_all habe ich nur paar 100KHz erreicht.

Tja . . .

von Mi N. (msx)


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.

: Bearbeitet durch User
von N. M. (mani)


Angehängte Dateien:

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.

von Vanye R. (vanye_rijan)


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

von Mi N. (msx)


Angehängte Dateien:

Lesenswert?

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

Einen habe ich noch übrig.

N. M. schrieb:
> Und wie schon oben gesagt: Soll es noch schneller sein nimm eine PIO.

Nicht unbedingt schneller aber auf jeden Fall komplizierter.

von Falk B. (falk)


Lesenswert?

Mi N. schrieb:

> rp2040.h (2,38 MB)
>> Und ja, es ist natuerlich schon schoener wenn man sich ein Headerfile
>> mit den Registern besorgt. .-)
>
> Einen habe ich noch übrig.

Ja, einen an der Waffel. Keiner braucht dein 2,4MB Headerfile, da ist 
bei jedem Compiler schon dabei.

von Vanye R. (vanye_rijan)


Lesenswert?

> da ist bei jedem Compiler schon dabei.

Bei meinem nicht. Ich musste mir das aus der kranken 
Entwicklungsumgebung
der RP2040 macher rausklauen.

Vanye

von Epi K. (epi_k)


Angehängte Dateien:

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.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Epi K. schrieb:
> void loop() {
>         gpio_put_all(0xffffffff);
>         gpio_put_all(0x00000000);
>       //sio_hw->gpio_set = mask;
>       //sio_hw->gpio_clr = mask;
>       //sio_hw->gpio_togl = mask;
> }

Hier ist dein Fehler. Loop wird zwar dauerhaft ausgeführt, ist aber eine 
Funktion, die aufgerufen wird. Ein while(1) {} ist da schneller.

von Epi K. (epi_k)


Angehängte Dateien:

Lesenswert?

Falk B. schrieb:
> Hier ist dein Fehler. Loop wird zwar dauerhaft ausgeführt, ist aber eine
> Funktion, die aufgerufen wird. Ein while(1) {} ist da schneller.

Tatsächlich, Signal ist immer noch nicht schön symetrisch aber jetzt bei 
31 Mhz mit put_all....

Tolle Sache :-)

von Falk B. (falk)


Angehängte Dateien:

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.

von Mi N. (msx)


Lesenswert?

Epi K. schrieb:
> Tatsächlich, Signal ist immer noch nicht schön symetrisch aber jetzt bei
> 31 Mhz mit put_all....

Wenn Du 100 MHz brauchst, lass den RP2040 mit 4xx MHz laufen.

Ich denke, jetzt ist der Zeitpunkt gekommen, zu sagen, was das ganze 
überhaupt soll.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.