Arbeiten Sie mit der Liste der Pins in C ++ für Mikrocontroller (am Beispiel von CortexM).


Gute Gesundheit an alle!


In einem früheren Artikel habe ich versprochen, darüber zu schreiben, wie Sie mit einer Liste von Ports arbeiten können.
Ich muss gleich sagen, dass bereits 2010 alles vor mir entschieden wurde, hier der Artikel: Arbeiten mit den Ein- / Ausgabeports von Mikrocontrollern in C ++ . Die Person, die dies 2010 geschrieben hat, sieht einfach gut aus.


Es war mir ein wenig peinlich, dass ich das tun würde, was bereits vor 10 Jahren getan wurde, also beschloss ich, nicht auf 2020 zu warten, sondern es 2019 zu tun, um die Entscheidung so lange wie vor 9 Jahren zu wiederholen, es wird nicht so dumm sein.


Im obigen Artikel wurde die Arbeit mit Typlisten mit C ++ 03 durchgeführt, wenn mehr Vorlagen eine feste Anzahl von Parametern hatten und Funktionen keine constexpr-Ausdrücke sein konnten. Seitdem hat sich C ++ ein wenig geändert. Versuchen wir also, dasselbe zu tun, jedoch in C ++ 17. Willkommen bei Katze:


Herausforderung


Die Aufgabe besteht also darin, mehrere Prozessor-Pins gleichzeitig zu installieren oder zu entfernen, die zu einer Liste zusammengefasst werden. Pins können sich an verschiedenen Ports befinden. Trotzdem sollte ein solcher Vorgang so effizient wie möglich durchgeführt werden.


Tatsächlich kann mit dem Code gezeigt werden, was wir tun möchten:


using Pin1 = Pin<GPIO, 1>; using Pin2 = Pin<GPIOB, 1>; using Pin3 = Pin<GPIOA, 1>; using Pin4 = Pin<GPIOC, 2>; using Pin5 = Pin<GPIOA, 3>; int main() { //    Pin    : //   GPIOA  10 GPIOA->BSRR = 10 ; // (1<<1) | (1 << 3) ; //   GPIOB  2 GPIOB->BSRR = 2 ; // (1 << 1) //   GPIOC  6 GPIOB->BSRR = 6 ; // (1 << 1) | (1 << 2); PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5>::Set() ; return 0; } 

Über das BSRR-Register

Für diejenigen, die sich der Angelegenheiten des Mikrocontrollers nicht bewusst sind, ist das GPIOA->BSRR Register für die atomare Installation oder das Zurücksetzen von Werten an den Beinen des Mikrocontrollers verantwortlich. Dieses Register ist 32 Bit. Die ersten 16 Bits sind für das Setzen von 1 an den Beinen verantwortlich, die zweiten 16 Bits für das Setzen von 0 an den Beinen.


Um beispielsweise den BSRR Nummer 3 auf 1 zu setzen, müssen Sie das dritte Bit im BSRR Register auf 1 BSRR . Um den BSRR Nummer 3 auf 0 zurückzusetzen, müssen Sie im selben BSRR Register 19 Bits auf 1 BSRR .


Ein verallgemeinertes Schema von Schritten zur Lösung dieses Problems kann wie folgt dargestellt werden:



Mit anderen Worten:


Für den Compiler:


  • Stellen Sie sicher, dass die Liste nur einen eindeutigen Pin enthält
  • Erstellen einer Liste von Ports durch Bestimmen, an welchen Ports sich der Pin befindet;
  • Berechnen des Wertes, der in jeden Port eingegeben werden soll

Und dann das Programm


  • Stellen Sie diesen Wert ein

Und Sie müssen dies so effizient wie möglich tun, damit der Code auch ohne Optimierung minimal ist. Eigentlich ist das die ganze Aufgabe.


Beginnen wir mit der ersten Modeerscheinung: Verifizieren, dass die Liste einen eindeutigen Pin enthält.


Überprüfen Sie die Liste auf Eindeutigkeit


Ich möchte Sie daran erinnern, dass wir eine Liste mit Pins haben:


 PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5> ; 

Versehentlich kann dies tun:


 PinsPack<Pin1, Pin2, Pin3, Pin4, Pin1> ; //     Pin1 

Ich möchte, dass der Compiler einen solchen Fehler auffängt und den Pianisten darüber informiert.


Wir werden die Liste wie folgt auf Eindeutigkeit prüfen:


  • Erstellen Sie aus der Quellliste eine neue Liste ohne Duplikate.
  • Wenn der Typ der Quellliste und der Typ der Liste ohne Duplikate nicht übereinstimmen, war die PIN in der Quellliste gleich und der Programmierer hat einen Fehler gemacht.
  • Wenn sie übereinstimmen, ist alles in Ordnung, es gibt keine Duplikate.

Um eine neue Liste ohne Duplikate zu erstellen, riet ein Kollege, das Rad nicht neu zu erfinden und den Ansatz aus der Loki-Bibliothek zu übernehmen. Ich habe diesen Ansatz und gestohlen. Fast das gleiche wie 2010, jedoch mit einer variablen Anzahl von Parametern.


Der Code, der von einem Kollegen ausgeliehen wurde, der die Idee von Loki ausgeliehen hat
 namespace PinHelper { template<typename ... Types> struct Collection { }; /////////////////   NoDuplicates   LOKI //////////////// template<class X, class Y> struct Glue; template<class T, class... Ts> struct Glue<T, Collection<Ts...>> { using Result = Collection<T, Ts...>; }; template<class Q, class X> struct Erase; template<class Q> struct Erase<Q, Collection<>> { using Result = Collection<>;}; template<class Q, class... Tail> struct Erase<Q, Collection<Q, Tail...>> { using Result = Collection<Tail...>;}; template<class Q, class T, class... Tail> struct Erase<Q, Collection<T, Tail...>> { using Result = typename Glue<T, typename Erase<Q, Collection<Tail...>>::Result>::Result;}; template <class X> struct NoDuplicates; template <> struct NoDuplicates<Collection<>> { using Result = Collection<>; }; template <class T, class... Tail> struct NoDuplicates< Collection<T, Tail...> > { private: using L1 = typename NoDuplicates<Collection<Tail...>>::Result; using L2 = typename Erase<T,L1>::Result; public: using Result = typename Glue<T, L2>::Result; }; ///////////////// LOKI //////////////// } 

Wie kann dies jetzt verwendet werden? Ja, es ist sehr einfach:


 using Pin1 = Pin<GPIOC, 1>; using Pin2 = Pin<GPIOB, 1>; using Pin3 = Pin<GPIOA, 1>; using Pin4 = Pin<GPIOC, 2>; using Pin5 = Pin<GPIOA, 3>; using Pin6 = Pin<GPIOC, 1>; int main() { //  Pin1  ,    Pin6      using PinList = Collection<Pin1, Pin2, Pin3, Pin4, Pin1, Pin6> ; using TPins = typename NoDuplicates<PinList>::Result; //  static_assert.        // : Collection<Pin1, Pin2, Pin3, Pin4, Pin1, Pin6> //   : Collection<Pin1, Pin2, Pin3, Pin4> // ,    static_assert(std::is_same<TPins, PinList>::value, ":    ") ; return 0; } 

Nun, d.h. Wenn Sie die Liste der Stifte falsch eingestellt haben und versehentlich zwei identische Stifte in der Liste angezeigt werden, wird das Programm nicht kompiliert und der Compiler gibt den folgenden Fehler aus: "Problem: Dieselben Stifte in der Liste."


Übrigens können Sie den folgenden Ansatz verwenden, um die korrekte Liste der Pins für Ports sicherzustellen:
 //        // PinsPack<Port<GPIOB, 0>, Port<GPIOB, 1> ... Port<GPIOB, 15>> using GpiobPort = typename GeneratePins<15, GPIOB>::type //      using GpioaPort = typename GeneratePins<15, GPIOA>::type int main() { //    :  GPIOA.0  1 Gpioa<0>::Set() ; // GPIOB.1  0 Gpiob<1>::Clear() ; using LcdData = Collection<Gpioa<0>, Gpiob<6>, Gpiob<2>, Gpioa<3>, Gpioc<7>, Gpioa<4>, Gpioc<3>, Gpioc<10>> ; using TPinsLcd = typename NoDuplicates<LcdData>::Result; static_assert(std::is_same<TPinsB, LcdData>::value, ":        LCD") ; // A      LcdData::Write('A'); } 

Wir haben hier bereits so viel geschrieben, aber es gibt bisher keine einzige Zeile echten Codes, die in den Mikrocontroller gelangt. Wenn alle Pins richtig gesetzt sind, sieht das Firmware-Programm folgendermaßen aus:


 int main() { return 0 ; } 

Fügen wir einen Code hinzu und versuchen, die Set() -Methode zum Setzen der Pins in der Liste zu verwenden.


Installationsmethode für Anschlussstifte


Lassen Sie uns kurz zum Ende der Aufgabe vordringen. Letztendlich ist es notwendig, die Set() -Methode zu implementieren, die automatisch basierend auf dem Pin in der Liste bestimmt, welche Werte an welchem ​​Port installiert werden sollen.


Der Code, den wir wollen
 using Pin1 = Pin<GPIOA, 1>; using Pin2 = Pin<GPIOB, 2>; using Pin3 = Pin<GPIOA, 2>; using Pin4 = Pin<GPIOC, 1>; using Pin5 = Pin<GPIOA, 3>; int main() { PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5>::Set() ; //      3   // GPIOA->BSRR = 14 ; // (1<<1) | (1 << 2) | (1 << 3) ; // GPIOB->BSRR = 4 ; // (1 << 2) // GPIOB->BSRR = 2 ; // (1 << 1); } 

Daher deklarieren wir eine Klasse, die eine Liste von Pins enthält, und definieren darin die öffentliche statische Methode Set() .


 template <typename ...Ts> struct PinsPack { using Pins = PinsPack<Ts...> ; public: __forceinline static void Set(std::size_t mask) { } } ; 

Wie Sie sehen, nimmt die Methode Set(size_t mask) einen Wert (mask) an. Diese Maske ist die Nummer, die Sie in die Ports eingeben müssen. Standardmäßig ist es 0xffffffff, was bedeutet, dass wir alle Pins in die Liste aufnehmen möchten (maximal 32). Wenn Sie dort einen anderen Wert übergeben, z. B. 7 == 0b111, sollten nur die ersten 3 Pins in der Liste installiert werden und so weiter. Das heißt Maske überlagert die Pin-Liste.


Portliste


Um etwas in die Pins installieren zu können, müssen Sie wissen, an welchen Ports sich diese Pins befinden. Jeder Pin ist an einen bestimmten Port gebunden, und wir können diese Ports aus der Pin-Klasse ziehen und eine Liste dieser Ports erstellen.


Unsere Pins sind verschiedenen Ports zugeordnet:


 using Pin1 = Pin<Port<GPIOA>, 1>; using Pin2 = Pin<Port<GPIOB>, 2>; using Pin3 = Pin<Port<GPIOA>, 2>; using Pin4 = Pin<Port<GPIOC>, 1>; using Pin5 = Pin<Port<GPIOA>, 3>; 

Diese 5 Pins haben nur 3 eindeutige Ports (GPIOA, GPIOB, GPIOC). Wenn wir eine Liste von PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5> wir eine Liste von drei Ports daraus Collection<Port<GPIOA>, Port<GPIOB>, Port<GPIOC>> : Collection<Port<GPIOA>, Port<GPIOB>, Port<GPIOC>>


Die Pin-Klasse enthält den Porttyp und sieht in vereinfachter Form folgendermaßen aus:


 template<typename Port, uint8_t pinNum> struct Pin { using PortType = Port ; static constexpr uint32_t pin = pinNum ; ... } 

Darüber hinaus müssen Sie noch eine Struktur für diese Liste definieren. Es handelt sich lediglich um eine Vorlagenstruktur, die eine variable Anzahl von Vorlagenargumenten akzeptiert


 template <typename... Types> struct Collection{} ; 

Jetzt definieren wir eine Liste eindeutiger Ports und prüfen gleichzeitig, ob die Liste der Pins nicht dieselben Pins enthält. Das ist ganz einfach:


 template <typename ...Ts> struct PinsPack { using Pins = PinsPack<Ts...> ; private: //      using TPins = typename NoDuplicates<Collection<Ts...>>::Result; //           static_assert(std::is_same<TPins, Collection<Ts...>>::value, ":    ") ; //     using Ports = typename NoDuplicates<Collection<typename Ts::PortType...>>::Result; ... } ; 

Mach weiter ...


Port List Bypass


Nachdem Sie die Liste der Ports erhalten haben, müssen Sie sie jetzt umgehen und mit jedem Port etwas unternehmen. In einer vereinfachten Form können wir sagen, dass wir eine Funktion deklarieren müssen, die eine Liste von Ports und eine Maske für die Liste der Pins am Eingang erhält.


Da wir eine Liste umgehen müssen, deren Größe nicht sicher bekannt ist, wird die Funktion eine Vorlage mit einer variablen Anzahl von Parametern sein.


Wir gehen "rekursiv" umher, solange die Vorlage noch Parameter enthält, rufen wir eine Funktion mit demselben Namen auf.


 template <typename ...Ts> struct PinsPack { using Pins = PinsPack<Ts...> ; private: __forceinline template<typename Port, typename ...Ports> constexpr static void SetPorts(Collection<Port, Ports...>, std::size_t mask) { // ,       if constexpr (sizeof ...(Ports) != 0U) { Pins::template WritePorts<Ports...>(Collection<Ports...>(), mask) ; } } } 

Wir haben also gelernt, wie man die Liste der Ports umgeht, aber zusätzlich zur Umgehung müssen Sie einige nützliche Arbeiten ausführen, nämlich etwas im Port installieren.


 __forceinline template<typename Port, typename ...Ports> constexpr static void SetPorts(Collection<Port, Ports...>, std::size_t mask) { //      auto result = GetPortValue<Port>(mask) ; //      Port::Set(result) ; if constexpr (sizeof ...(Ports) != 0U) { Pins::template WritePorts<Ports...>(Collection<Ports...>(), mask) ; } } 

Diese Methode wird zur Laufzeit ausgeführt, da der mask von außen an die Funktion übergeben wird. SetPorts() wir nicht garantieren können, dass eine Konstante an die SetPorts() -Methode übergeben wird, wird die GetValue() -Methode auch zur Laufzeit ausgeführt.


Und obwohl in dem Artikel Arbeiten mit Eingabe- / Ausgabeports von Mikrocontrollern in C ++ geschrieben steht, dass der Compiler in einer ähnlichen Methode festgestellt hat, dass eine Konstante übergeben wurde, und den Wert zum Schreiben in den Port in der Kompilierungsphase berechnet hat, hat mein Compiler einen solchen Trick nur bei maximaler Optimierung ausgeführt.
Ich möchte, dass GetValue() Kompilierungszeit mit allen Compilereinstellungen ausgeführt wird.


Ich habe im Standard nicht gefunden, wie der Compiler den Compiler in diesem Fall führen soll, aber wenn man bedenkt, dass der IAR-Compiler dies nur bei maximaler Optimierung ausführt, ist dies höchstwahrscheinlich nicht durch den Standard geregelt oder sollte nicht als Constexpr-Ausdruck aufgefasst werden.
Wenn jemand weiß, schreibe in die Kommentare.


Um die explizite Übertragung eines konstanten Werts sicherzustellen, erstellen wir eine zusätzliche Methode mit übergebener mask in der Vorlage:


 __forceinline template<std::size_t mask, typename Port, typename ...Ports> constexpr static void SetPorts(Collection<Port, Ports...>) { using MyPins = PinsPack<Ts...> ; //    compile time,    value    constexpr auto result = GetPortValue<Port>(mask) ; Port::Set(result) ; if constexpr (sizeof ...(Ports) != 0U) { MyPins::template SetPorts<mask,Ports...>(Collection<Ports...>()) ; } } 

Auf diese Weise können wir jetzt die Liste der Pins durchgehen, die Ports aus ihnen herausziehen und eine eindeutige Liste der Ports erstellen, an die sie gebunden sind, und dann die erstellte Liste der Ports durchgehen und den erforderlichen Wert für jeden Port festlegen.
Dieser Wert muss noch berechnet werden .


Berechnung des im Port einzustellenden Wertes


Wir haben eine Liste von Ports, die wir aus der Pin-Liste erhalten haben. In unserem Beispiel ist dies eine Liste: Collection<Port<GPIOA>, Port<GPIOB>, Port<GPIOC>> .
Sie müssen ein Element dieser Liste verwenden, z. B. den GPIOA-Port. Suchen Sie dann in der Pin-Liste alle Pins, die an diesen Port angeschlossen sind, und berechnen Sie den Wert für die Installation im Port. Und dann machen Sie dasselbe mit dem nächsten Port.


Nochmals: In unserem Fall lautet die Liste der Pins, von denen Sie eine Liste der eindeutigen Ports abrufen müssen, wie folgt:
 using Pin1 = Pin<Port<GPIOC>, 1>; using Pin2 = Pin<Port<GPIOB>, 1>; using Pin3 = Pin<Port<GPIOA>, 1>; using Pin4 = Pin<Port<GPIOC>, 2>; using Pin5 = Pin<Port<GPIOA>, 3>; using Pins = PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5> ; 

Für den GPIOA-Port sollte der Wert also (1 << 1 ) | (1 << 3) = 10 (1 << 1 ) | (1 << 3) = 10 und für den GPIOC-Port - (1 << 1) | (1 << 2) = 6 (1 << 1) | (1 << 2) = 6 und für GPIOB (1 << 1 ) = 2


Die Berechnungsfunktion akzeptiert den angeforderten Port. Befindet sich Pin auf demselben Port wie der angeforderte Port, muss die Maske in der Maske entsprechend der Position dieses Pina in der Liste, Einheit (1), festgelegt werden.
Es ist nicht einfach, es in Worten zu erklären. Es ist besser, direkt in den Code zu schauen:


 template <typename ...Ts> struct PinsPack { using Pins = PinsPack<Ts...> ; private: __forceinline template<class QueryPort> constexpr static auto GetPortValue(std::size_t mask) { std::size_t result = 0; //  ,       // 1. ,        // 2.            // e (.     ), ,  Pin   0  //        10,      //    ( )  (1 << 10)    // 3.    1   // 4.   1-3      pass{(result |= ((std::is_same<QueryPort, typename Ts::PortType>::value ? 1 : 0) & mask) * (1 << Ts::pin), mask >>= 1)...} ; return result; } } ; 

Festlegen des für jeden Port berechneten Werts für Ports


Jetzt kennen wir den Wert, der in jedem Port eingestellt werden muss. Es bleibt noch die public Set() -Methode zu vervollständigen, die für den Benutzer sichtbar ist, so dass die gesamte Wirtschaftlichkeit aufgerufen wird:


 template <typename ...Ts> struct PinsPack { using Pins = PinsPack<Ts...> ; __forceinline static void Set(std::size_t mask) { //        SetPorts(Ports(), mask) ; } } 

Wie im Fall von SetPorts() eine zusätzliche Template-Methode SetPorts() , um die Übertragung der mask als Konstante zu gewährleisten, die im Template-Attribut übergeben wird.


 template <typename ...Ts> struct PinsPack { using Pins = PinsPack<Ts...> ; //    0xffffffff,      32  __forceinline template<std::size_t mask = 0xffffffffU> static void Set() { SetPorts<mask>(Ports()) ; } } 

In der endgültigen Form sieht unsere Klasse für die Pin-Liste folgendermaßen aus:
 using namespace PinHelper ; template <typename ...Ts> struct PinsPack { using Pins = PinsPack<Ts...> ; private: using TPins = typename NoDuplicates<Collection<Ts...>>::Result; static_assert(std::is_same<TPins, Collection<Ts...>>::value, ":    ") ; using Ports = typename NoDuplicates<Collection<typename Ts::PortType...>>::Result; template<class Q> constexpr static auto GetPortValue(std::size_t mask) { std::size_t result = 0; auto rmask = mask ; pass{(result |= ((std::is_same<Q, typename Ts::PortType>::value ? 1 : 0) & mask) * (1 << Ts::pin), mask>>=1)...}; pass{(result |= ((std::is_same<Q, typename Ts::PortType>::value ? 1 : 0) & ~rmask) * ((1 << Ts::pin) << 16), rmask>>=1)...}; return result; } __forceinline template<typename Port, typename ...Ports> constexpr static void SetPorts(Collection<Port, Ports...>, std::size_t mask) { auto result = GetPortValue<Port>(mask) ; Port::Set(result & 0xff) ; if constexpr (sizeof ...(Ports) != 0U) { Pins::template SetPorts<Ports...>(Collection<Ports...>(), mask) ; } } __forceinline template<std::size_t mask, typename Port, typename ...Ports> constexpr static void SetPorts(Collection<Port, Ports...>) { constexpr auto result = GetPortValue<Port>(mask) ; Port::Set(result & 0xff) ; if constexpr (sizeof ...(Ports) != 0U) { Pins::template SetPorts<mask, Ports...>(Collection<Ports...>()) ; } } __forceinline template<typename Port, typename ...Ports> constexpr static void WritePorts(Collection<Port, Ports...>, std::size_t mask) { auto result = GetPortValue<Port>(mask) ; Port::Set(result) ; if constexpr (sizeof ...(Ports) != 0U) { Pins::template WritePorts<Ports...>(Collection<Ports...>(), mask) ; } } __forceinline template<std::size_t mask, typename Port, typename ...Ports> constexpr static void WritePorts(Collection<Port, Ports...>) { Port::Set(GetPortValue<Port>(mask)) ; if constexpr (sizeof ...(Ports) != 0U) { Pins::template WritePorts<mask, Ports...>(Collection<Ports...>()) ; } } public: static constexpr size_t size = sizeof ...(Ts) + 1U ; __forceinline static void Set(std::size_t mask ) { SetPorts(Ports(), mask) ; } __forceinline template<std::size_t mask = 0xffffffffU> static void Set() { SetPorts<mask>(Ports()) ; } __forceinline static void Write(std::size_t mask) { WritePorts(Ports(), mask) ; } __forceinline template<std::size_t mask = 0xffffffffU> static void Write() { WritePorts<mask>(Ports()) ; } } ; 

Infolgedessen kann das Ganze wie folgt verwendet werden:


 using Pin1 = Pin<GPIOC, 1>; using Pin2 = Pin<GPIOB, 1>; using Pin3 = Pin<GPIOA, 1>; using Pin4 = Pin<GPIOC, 2>; using Pin5 = Pin<GPIOA, 3>; using Pin6 = Pin<GPIOA, 5>; using Pin7 = Pin<GPIOC, 7>; using Pin8 = Pin<GPIOA, 3>; int main() { //1.   ,     3 ,  : // GPIOA->BSRR = (1 << 1) | (1 << 3) // GPIOB->BSRR = (1 << 1) // GPIOC->BSRR = (1 << 1) | (1 << 2) PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5>::Set() ; //   Set<0xffffffffU>() //2.   ,  3 ,  : // GPIOA->BSRR = (1 << 1) // GPIOB->BSRR = (1 << 1) // GPIOC->BSRR = (1 << 1) | (1 << 2) PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5, Pin6>::Set<7>() ; //3.          , //   someRunTimeValue     ,  //  SetPorts   constexpr    PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5>::Set(someRunTimeValue) ; using LcdData = PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7, Pin8> ; LcdData::Write('A') ; } 

Ein vollständigeres Beispiel finden Sie hier:
https://onlinegdb.com/r1eoXQBRH


Leistung


Wie Sie sich erinnern, wollten wir unseren Anruf in 3 Leitungen umwandeln, die auf Port A 10, Port B - 2 und Port C - 6 eingestellt sind


 using Pin1 = Pin<GPIO, 1>; using Pin2 = Pin<GPIOB, 1>; using Pin3 = Pin<GPIOA, 1>; using Pin4 = Pin<GPIOC, 2>; using Pin5 = Pin<GPIOA, 3>; int main() { //    Pin    : //   GPIOA  10 GPIOA->BSRR = 10 ; // (1<<1) | (1 << 3) ; //   GPIOB  2 GPIOB->BSRR = 2 ; // (1 << 1) //   GPIOC  6 GPIOB->BSRR = 6 ; // (1 << 1) | (1 << 2); PinsPack<Pin1, Pin2, Pin3, Pin4, Pin5>::Set() ; return 0; } 

Mal sehen, was mit der vollständig deaktivierten Optimierung passiert ist.



Ich habe die Portwerte und Aufrufe getönt, um diese Werte auf Ports in Grün zu setzen. Es ist ersichtlich, dass alles wie beabsichtigt ausgeführt wird. Der Compiler für jeden der Ports berechnete den Wert und rief einfach die Funktion auf, um diese Werte auf die erforderlichen Ports zu setzen.
Wenn die Installationsfunktionen auch inline ausgeführt werden, erhalten wir am Ende einen Aufruf, um den Wert für jeden Port in das BSRR-Register zu schreiben.


Eigentlich ist das alles. Wen kümmert es, der Code ist hier .


Ein Beispiel ist hier .


https://onlinegdb.com/ByeA50wTS

Source: https://habr.com/ru/post/de474732/


All Articles