Frage:
Stellen Sie den STM32 GPIO-Takt und die Datenpins so schnell wie möglich ein
Randomblue
2012-07-30 17:15:07 UTC
view on stackexchange narkive permalink

Ich habe einen STM32, der neun GPIO-Pins wiederholt umschaltet (einen Takt-Pin und acht Daten-Pins zum Laden eines FPGA-Images mit SelectMap). Ich mache dies mit der Standardbibliotheksfunktion GPIO_WriteBit , die ein GPIO-Bit modifiziert und jeweils einen Pin ändert.

Leider ist dies ziemlich langsam. Gibt es eine Möglichkeit, wie ich sehr schnell "rohe GPIO-Umschaltungen" vornehmen kann? Gibt es einen Uhrparameter, den ich ändern sollte? Kann ich eine Art FIFO oder eine Interrupt-basierte Methode verwenden?

Ich habe die GPIO-Pins wie folgt konfiguriert:

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  

(Hinweis: Das Referenzhandbuch (siehe Seite 133) gibt an, dass das GPIO in der Lage ist:

  Schnelles Umschalten, das alle zwei Taktzyklen geändert werden kann  

Aber ich kann nicht sehen, wie dieses schnelle Umschalten aktiviert wird.)

Warum ändern Sie jedes Bit nach dem anderen? Wenn Sie schnell laden möchten, sollten Sie stattdessen "GPIO_Write" verwenden.
@OliGlaser: Nicht alle Pins befinden sich auf derselben GPIO-Bank. Diejenigen in derselben Bank werden zur gleichen Zeit geschrieben.
Ah okay, dann verwenden Sie stattdessen die GPIOx-> ODR-Register. Stellen Sie außerdem sicher, dass Ihr APB-Vorteiler auf 1 gesetzt ist (keine Teilung).
Ist das ein STM32F4?
@OliGlaser: Es ist ein STM32F2, und ich habe sichergestellt, dass beide geeigneten APB-Vorskalierer (der APB- und der APB1-Vorskalierer) auf 1 gesetzt sind
@Randomblue- Sicher, dass APB richtig ist? Gemäß der Tabelle auf Seite 49 des Datenblattes befindet sich GPIO auf AHB1.
@Steven - Sie haben Recht, für diesen STM32 befinden sie sich auf AHB1 (siehe auch Seite 109). Ich habe meinen STM32F1-Code überprüft und angenommen, dass sie wahrscheinlich gleich sind.
Fünf antworten:
supercat
2015-01-30 23:47:17 UTC
view on stackexchange narkive permalink

Das Setzen oder Löschen einer beliebigen Kombination von Bits in einem E / A-Port (sogar das Setzen einiger und das Löschen anderer) sollte höchstens drei Anweisungen erfordern. Bei Wiederholung sollten solche Anweisungen jeweils eine Anweisung enthalten. Leider scheinen viele Anbieter (darunter auch ST) dazu zu neigen, E / A-Bibliotheken zu definieren, die selbst für allgemeine Vorgänge wie diesen Unterprogrammaufrufe generieren.

Ich würde vorschlagen, eigene Methoden zum Festlegen und Löschen von E / A zu definieren. O-Bits und enthalten den Code für diese Methoden, der mit einem __inline -Qualifikator gekennzeichnet ist, in Ihrer .h-Datei [im Gegensatz zum bloßen Einfügen eines Prototyps in die .h und des Methodencodes in einem separaten Code. c-Datei].

Wenn Sie sich in einer .h-Datei befinden, schreiben Sie:

  __inline void SetPorts (GPIO_TypeDef * Addr, uint16_t data) {* Addr = data; } __ inline void ClearPorts (GPIO_TypeDef * Addr, uint16_t Daten) {* Addr = Daten; }  

Dann der Code:

  setPorts (GPIOA, 1); clearPorts (GPIOA, 2); clearPorts (GPIOA, 1);  

wird wahrscheinlich etwas erzeugen (ich bin mir einiger Details nicht sicher):

  ldr r0, = GPIOA mov r1, # 1 strh r1, [r0 + 20] mov r2, # 2 strh r2, [r0 + 22] strh r1, [r0 + 22]  

Wenn man dagegen den Code GPIO_WriteBit verwendet würde am Ende eher wie folgt aussehen:

  ldr r0, = GPIOA mov r1, # 1 mov r2, # 1 bl _GPIO_WriteBit ldr r0, = GPIOA mov r1, # 2 mov r2, # 0 bl _GPIO_WriteBit ldr r0, = GPIOA mov r1, # 2 mov r2, # 0 bl _GPIO_WriteBit_GPIO_WriteBit :; Der folgende Code setzt eine ziemlich gute Compileroptimierung voraus: cmp r2, # 0 itteq streq r1, [r0 + 20] strne r1, [r0 + 22] bx lr  

Im ersten Beispiel werden insgesamt sechs Anweisungen für alle drei Operationen ausgeführt. Im zweiten Beispiel werden zwölf im Hauptcode einmal und ungefähr vier Anweisungen in der WriteBits-Funktion dreimal ausgeführt, was insgesamt 24 ergibt [übersprungene Anweisungen verlängern manchmal die Ausführungszeit und manchmal nicht]. Normalerweise besteht der Zweck des Aufrufs von Unterroutinen darin, die Codegröße gegen die Ausführungsgeschwindigkeit auszutauschen. In diesem Fall ist der Aufruf der Unterroutine jedoch sowohl räumlich als auch zeitlich eine Katastrophe. Die einzige nützliche Arbeit, die durch die Anweisung geleistet wird, ist ein einzelner Geschäftsvorgang. Der Code wird wahrscheinlich die Register r0-r2 ungestört lassen, aber der aufrufende Code kann das nicht wissen. Folglich müssen alle drei Parameter vor jedem Methodenaufruf explizit festgelegt werden.

Ich weiß nicht, warum Chiphersteller Methoden definieren, die GPIO-Bits schreiben, sich aber nicht die Mühe machen, sie inline zu machen, aber ich würde vorschlagen In den meisten Fällen sollte man vermeiden, vom Chip-Anbieter bereitgestellte Funktionen zum Schreiben von GPIO-Ports zu verwenden, wenn man sich um die Effizienz kümmert.

Übrigens bin ich generell eher skeptisch gegenüber vom Hersteller bereitgestellten E / A-Funktionen im Allgemeinen. Während sie manchmal einen Programmierer mit einem höheren Abstraktionsgrad als die Rohhardware präsentieren können, was nützlich ist, machen sie Code in vielen Fällen schwieriger zu schreiben, schwerer zu lesen und weniger effizient. Sie können auch manchmal unerwünschte Nebenwirkungen haben, die an anderer Stelle Probleme verursachen können. Wenn für ein Peripheriegerät beispielsweise eine Uhr auf einen von zwei Modi eingestellt werden muss und im Allgemeinen mit einem dieser Modi besser funktioniert, kann eine vom Hersteller bereitgestellte Bibliothek für das Peripheriegerät die Uhr auf den "besseren" Modus konfigurieren, selbst wenn diese Uhr aktiviert ist mit einem anderen Peripheriegerät geteilt, das es benötigt, um in dem anderen zu sein. Wenn Peripheriegeräte Ressourcen gemeinsam nutzen (wie dies häufig der Fall ist), ist es möglicherweise unmöglich, die Bibliotheken ordnungsgemäß zu verwenden, ohne den gesamten darin enthaltenen Code zu lesen und zu verstehen. Wenn der gesamte Zweck der Bibliothek darin bestand, ein paar Registerschreibvorgänge zu speichern, sind die Konsequenzen eines Bibliotheksaufrufs möglicherweise viel schwieriger herauszufinden als die Konsequenzen des Codes, den sie ersetzt.

stevenvh
2012-07-30 21:13:13 UTC
view on stackexchange narkive permalink

Ich habe auf Ihre verwandte Frage geantwortet, warum Sie nur bei 4 MHz umschalten konnten, wenn Sie 100 MHz erwartet hatten:

Wenn die 4 MHz ungefähr 4 MHz sind, und Nicht genau 100 MHz / 25, dann liegt das Problem wahrscheinlich bei der C-Funktion GPIO_WriteBit .

Für Hochgeschwindigkeitsoperationen und Operationen, die ein genaues Timing erfordern, ist ein besserer Code bei der Montage als bei C. Wenn Sie sich den von GPIO_WriteBit erstellten Assemblycode ansehen, kann er eine halbe Seite lang sein, je nachdem, welche Funktionen die Funktion bietet und wie viel der Optimierer des Compilers damit tun kann.

Sie sagen nicht, welche Entwicklungs-Toolchain Sie verwenden, aber viele / die meisten C-Compiler können Inline-Assemblys verarbeiten.

Schreiben Sie also die Funktionen in Assembly. Eine Funktion wie

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  

sollte in der Assemblierung nicht mehr als 2 Anweisungen benötigen, während der kompilierte C-Code 20-mal dauern kann viel. Oder mehr.

Also habe ich den Code neu geschrieben, indem ich direkt in die Register geschrieben und die Schleife entpackt habe, und ich habe ihn auf 20 MHz gebracht, aber er ist immer noch nicht annähernd 100 MHz.
RobC
2014-11-02 04:27:42 UTC
view on stackexchange narkive permalink

Die von Ihnen eingestellte Geschwindigkeit steuert nur die Anstiegsgeschwindigkeit des Stifts. Je schneller es ist, desto schneller steigt die Flanke. Es gibt nicht direkt an, wie schnell Sie die Ports umschalten können.

Mit dieser Funktion können Sie eine geeignete Schnittstelle zu anderen Geräten herstellen, für die bestimmte Anstiegs- / Abfallzeiten erforderlich sind.

diverger
2014-11-02 06:05:37 UTC
view on stackexchange narkive permalink

Sie stellen die Geschwindigkeit der GPIOs auf 100 MHz ein. Dies ist die Geschwindigkeitsbegrenzung, die die Hardware unterstützen kann. Die endgültige Datenaustauschgeschwindigkeit kann jedoch auch dadurch begrenzt sein, wie schnell Ihr Code ausgeführt werden kann, da die Datentaktung usw. jetzt von Ihrem Code gesteuert wird.

Meiner Meinung nach unterstützen nicht alle Mitglieder der STM32-Familie die GPIO-Geschwindigkeit von 100 MHz, und die MCU-Geschwindigkeit einiger Familien ist niedriger als diese Geschwindigkeit, z. B. 72 MHz. Wenn Ihre MCU 100-MHz-GPIO unterstützt, optimieren Sie Ihren Code. Andernfalls wählen Sie eine MCU mit einer höheren Taktrate. STM32F429 unterstützt eine Taktrate von 180 MHz.

Überprüfen Sie das Datenblatt der MCU. Im Abschnitt "Definition der E / A-AC-Eigenschaften" werden die elektrischen Eigenschaften des GPIO und die Definition von "100 MHz" angegeben. Und Sie sollten beachten: " Für maximale Frequenzen über 50 MHz sollte die Kompensationszelle verwendet werden. "

Steve
2015-01-30 12:25:10 UTC
view on stackexchange narkive permalink

Stellen Sie sicher, dass auch Ihre Uhrenregister richtig eingestellt sind. Laden Sie das STM32F-Uhrenkonfigurationstool herunter.

Auch der STM32F-Cube verfügt über einen automatischen Uhrengenerator. Sie müssen diese korrigieren, sonst erhalten Sie keine guten (oder keine) Ergebnisse.

Wenn Sie sich keine Sorgen über die Taktdrift machen, skalieren Sie den externen Takt auf 1 MHz und verwenden Sie dann die anderen Register, um die gewünschten Ergebnisse zu erzielen.



Diese Fragen und Antworten wurden automatisch aus der englischen Sprache übersetzt.Der ursprüngliche Inhalt ist auf stackexchange verfügbar. Wir danken ihm für die cc by-sa 3.0-Lizenz, unter der er vertrieben wird.
Loading...