Zum Thema Multiplikation, Quadratwurzelextraktion, Importsubstitution und der Firma Milander

„Entropie, eine ergodische Quelle, ein mehrdimensionaler Nachrichtenraum, Bits, Polysemie, der Markov-Prozess - all diese Wörter klingen sehr beeindruckend, in welcher Reihenfolge auch immer sie platziert sind. Wenn Sie sie in der richtigen Reihenfolge anordnen, erhalten sie einen bestimmten theoretischen Inhalt. Und manchmal kann ein echter Spezialist mit seiner Hilfe eine Lösung für alltägliche praktische Probleme finden. “

John PIRS "Ich sehe kein Übel"


Dieser Beitrag ist voll von Diskussionen über die subtile Optimierung mathematischer Operationen auf MK mit begrenzten Ressourcen sowie subjektiven Bewertungen verschiedener Aspekte der Entwicklung eingebetteter Software.

Diejenigen, die diese Warnung nicht erschreckte, frage ich unter Katze.

Bevor wir das Verfahren zum Extrahieren einer Quadratwurzel aus einer ganzen Zahl beschreiben, die Operation umgekehrt zum Quadrieren und dementsprechend zum Multiplizieren, lassen Sie uns über letzteres sprechen.

Angenommen, wir haben die Möglichkeit, eine 8-Bit-Zahl mit einer 8-Bit-Zahl zu multiplizieren und ein 16-Bit-Ergebnis (8 * 8 = 16) zu erhalten. Wie können wir die Implementierung der Operation 16 * 16 = 32 basierend auf dieser Operation erhalten? Der offensichtliche Weg ist, 16 als die Summe von zwei 8 darzustellen, dann erhalten wir

(16)*(16)=(1(8)*256+2(8))*1(8)*256+2(8)) =1*1*256*256+1*2*256+2*1*256+2*2

Wenn wir im resultierenden Ausdruck die Multiplikation durch 256 durch eine Linksverschiebung um 8 Stellen ersetzen, erhalten wir einen vollständig funktionierenden Algorithmus. Schätzen wir den Zeitaufwand für die Implementierung - wir benötigen 4 Multiplikationen von 8 * 8 = 16 und 4 Additionen von 4 Byte-Nummern 32 + 32 = 32. Für AVR vom MK-Typ erhalten wir 4 * 2 + 4 * 4 = 24 Zyklen, dies gilt jedoch für eine "Stirn" -Lösung. Versuchen wir, das Ergebnis zu verbessern. Die Tatsache, dass wir nicht 4, sondern 3 Additionen und eine Zuordnung benötigen, vereinfacht die Situation etwas, da das anfängliche Nullstellen des Ergebnisses nicht erforderlich ist, wir es jedoch immer noch nicht berücksichtigt haben, obwohl es notwendig war und die Gesamtzeit 24 + 4 = 28 Zyklen betragen sollte. Wenn wir jedoch das Vorhandensein einer Verschiebung in den ersten drei Termen berücksichtigen (wir haben jeweils, dass das Tief (zwei niedrige Bytes) Null ist und es keinen Sinn macht, es zum Ergebnis hinzuzufügen), müssen wir nicht 4 Bytes hinzufügen, sondern drei und zwei, was sich verringert Ausführungszeit für 1 * 2 + 2 = 4 Takte und 20 Takte. Ferner können wir darauf achten, dass sich der erste und der letzte Term überhaupt nicht überschneiden, was es uns ermöglicht, die Nullstellung der oberen Hälfte des Ergebnisses durch die Zuweisung des ersten Terms zu ersetzen und die Ausführungszeit um weitere 2 Taktzyklen auf 18 zu reduzieren. Weiter unter Verwendung der Architekturmerkmale, nämlich des Vorhandenseins des Registerübertragungsbefehls Paare, speichern Sie zwei weitere Takte und das Endergebnis - 16 Takte anstelle der ursprünglichen 28 - eine Kleinigkeit, aber schön.

Ähnliche Optimierungsmethoden funktionieren für die Operation 32 * 32 = 32, für die Sie die Ausführungszeit von den erwarteten 4 * 4 * (2 + 4) + 4 = 100 Taktzyklen auf (3 + 5 + 4 + 3) + (5 + 3) reduzieren können +3) + (4 + 3) + 3 = 36 Maßnahmen, was überhaupt nicht schlecht ist. Nun, am Ende der Betrachtung verschiedener Multiplikationsoptionen stellen wir fest, dass 16 * 16 = 16 in 3 + 3 + 3 = 9 Zyklen erhalten werden kann. Beachten Sie, dass alle diese Überlegungen nur unter der Annahme gültig sind, dass es eine Operation 8 * 8 = 16 für 2 Takte gibt. Wenn sie sich nicht auf dem Ziel-MK befindet, wird die Ausführungszeit aller anderen Versionen der Operation definitiv nicht schneller.

Fassen wir die zur Durchführung der Multiplikation erforderliche Zeit zusammen (8 * 8 = 8 2, 8 * 8 = 16 9, 16 * 16 = 16 16, 16 * 16 = 32 36) und betrachten wir nun das ursprüngliche Problem.

Wir müssen die quadratische Ganzzahlwurzel aus der 32-Bit-Zahl H extrahieren, dh die größte 16-Bit-Zahl n finden, so dass n * n <= H. Wir alle aus dem Sekundarschulkurs kennen die Methode der sukzessiven Approximation an die Quadratwurzel (n = (N / n '+ n) / 2), aber wenn wir sie verwenden, müssen wir die ganzen Zahlen teilen, und dies ist eine sehr zeitaufwändige Operation.

Daher wurden andere Berechnungsschemata entwickelt, von denen eines die bitweise Approximationsmethode ist, die im Pseudocode wie folgt aussieht:

  • Anfangswerte -> n = 0; b = 0x8000;
  • 16 mal ausführen -> wenn ((n + b) * (n + b)> = H) n = n + b; b = b >> 1;

Sie können die für diese Option aufgewendete Zeit 16 (Anzahl der Bits des Ergebnisses) * (2 (Organisation des Zyklus) +2 (Addition) + X (Multiplikation) +5 (Vergleich und Lösung) +2 (Änderung des Ergebnisses) / 2 (Durchschnitt) sofort schätzen Halbzeit) +2 (Bitverschiebung)) = 16 * (12 + X). Sie fragen, warum in der Formel X anstelle der Zahl 16, und es stellt sich heraus, dass ein Hinterhalt auf uns wartete, da wir in C und nicht in Assembler schreiben. Tatsache ist, dass es in der Standardbibliothek keine Multiplikationsoperation mit einer Änderung der Bittiefe gibt und wir nicht 16 * 16 = 32 anwenden können, sondern 32 * 32 = 32 verwenden müssen, was zu X = 36 anstelle von X = 16 führt und die endgültige Zahl 16 * ist 48 = 768 Taktzyklen zum Extrahieren des ganzzahligen Werts der Quadratwurzel einer 32-Bit-Zahl.

Das ist natürlich viel besser als die Newton-Methode, aber ein bisschen viel, mal sehen, was getan werden kann.
Es ist also offensichtlich, dass die meiste Zeit für die Berechnung des nächsten Multiplikationsergebnisses aufgewendet wird. Natürlich können Sie es in Assembler umschreiben und die kostengünstigere Version der Multiplikation verwenden, um 16 * (12 + 16) = 448 Ticks zu erhalten, aber wir werden dies als letzten Ausweg belassen. Betrachten Sie den Prozess genauer und stellen Sie fest, dass wir nicht die Multiplikation einer Zufallszahl selbst berechnen, sondern die Multiplikation des vorherigen Werts mit einer gewissen Zunahme, und das Quadrat des vorherigen Werts ist bekannt. Daher können wir auf ein Differenzschema zurückgreifen, das auf der Formel (n + b) * (n + b) = n * n + 2 * n * b + b * b basiert. Auf den ersten Blick sieht es wie ein Spott aus - statt einer Multiplikation müssen wir vier Teile und sogar zwei Additionen von langen (32-Bit) Zahlen machen. Aber fangen wir an zu verstehen: Wir haben bereits n * n, b * b, wenn man berücksichtigt, dass b = b '/ 2 leicht zu erhalten ist, wie b' * b '/ 4 und ähnlich 2 * n * b = 2 * n * b '/ 2.

Das folgende Berechnungsschema ergibt sich:

  1. Anfangswerte -> nn = 0; n = 0; b = 0x8000; bb = b * b;
  2. 16 mal wiederholen -> wenn (nn + n + bb> = H) {n = n + b; nn = nn + bb + n}; bb >> 2; b> 1;

Wir schätzen die Implementierungskosten auf 16 * (2 (Organisation des Zyklus) +12 (Zuordnung und zwei Ergänzungen) +5 (Vergleich und Lösung) + (2 (Addition) +8 (zwei Ergänzungen)) / 2 (durchschnittliche Halbzeit) +8 (Verschiebung nach rechts um 2) +2 (Verschiebung nach rechts) = 16 * 34 = 544 Taktzyklen. Besser als bei falscher Multiplikation von 32 * 32, aber wir haben immer noch Reserven.

Was sind sie - lassen Sie uns auf die teuerste Operation achten - addieren und vergleichen Sie insgesamt 17 Taktzyklen und wiederholen Sie die Hauptschleife des Algorithmus:
2. 16 mal wiederholen -> T = H-bb-n; wenn (T> = 0) {H = T; n = n + b);}; bb >> 2; b> 1;
Dann beträgt die Ausführungszeit des Zyklus 16 * (2 (Organisation des Zyklus) +12 (Berechnung der neuen Differenz) +1 (Vergleich und Lösung) + ((4 (Zuordnung) +2 (Addition)) / 2 (durchschnittliche Halbzeit) +8 +2) = 16 * 28 = 448 Zyklen Wenn Sie die Besonderheiten der Architektur berücksichtigen, können Sie weitere 2 + 2 = 4 * 16 = 64 Zyklen speichern und innerhalb von weniger als 400 Zyklen halten.

Wir erhalten sogar ein etwas besseres Ergebnis, als wenn wir die korrekte Multiplikation 16 * 16 = 32 verwenden, jedoch ohne Assembler "in reinem C". Es gibt jedoch ein signifikantes Minus - wenn in der Version mit Multiplikation alles intuitiv ist, dann vermittelt die Variante mit einem Differenzschema ohne Kommentare den Eindruck einer Sitzung schwarzer Magie, die Sie wählen sollten. Beachten Sie auch, dass wir die Anzahl der Kennzahlen für zusätzlichen Speicher gegen Zwischenvariablen ausgetauscht haben, was normalerweise der Fall ist.

Notwendiger Hinweis - Wir haben im Vergleich zu Multiplikationen (zeitweise) keinen signifikanten Gewinn erzielt, da wir eine schnelle Implementierung von 8 * 8 = 16 haben. Wenn es im MK fehlt (und dies passiert) oder nicht so schnell (und das passiert auch), wird das Differenzschema um ein Vielfaches schneller, da nur Standardadditions- und -verschiebungsoperationen verwendet werden, die garantiert in jedem MK vorhanden sind.

Es schien, dass es nicht besser funktionieren würde, aber es stellt sich heraus, dass es immer noch Reserven gibt, um die Leistung des Algorithmus zu steigern. Versuchen wir, eine andere klassische Beschleunigungsmethode zu verwenden - Teilen und Erobern. Was ist, wenn Sie zuerst die Quadratwurzel aus der älteren Hälfte des Arguments extrahieren und dann verfeinern? Zunächst zeigen wir, dass dies grundsätzlich möglich ist. In der Tat präsentieren wir das Argument in der Form H = H '<< 16 + H' 'und das Ergebnis in der Form n = n' << 8 + n ''. Da n '' <256 ist, ist sein Quadrat offensichtlich kleiner als das Quadrat der Zahl n = n '<< 8 + 256 = (n' + 1) << 8. Daraus folgt, dass der höchste Teil des Ergebnisses die Quadratwurzel des höchsten Teils des Arguments nicht überschreitet.

Die Umsetzung dieses Ansatzes bleibt dem neugierigen Leser überlassen.
Was bringt uns dieser Ansatz, weil die Gesamtzahl der Iterationen unverändert bleibt - wir können die erste Hälfte der Iterationen mit kürzeren Längen durchführen, was zu einer Verringerung der Zeitkosten führt. Dieser Ansatz kann auf die Variante mit Multiplikation und die Differenzvariante angewendet werden. Der Gesamtgewinn beträgt bis zu einem Viertel der gesamten Ausführungszeit.

Notwendiger Hinweis: Die Anwendbarkeit dieses Ansatzes ist überhaupt nicht offensichtlich, wenn bei der Implementierung für MKs wie AVR eine Ausführungsbeschleunigung stattfindet. Bei einigen Architekturen, z. B. für x86, trat jedoch eine unerwartete Verlangsamung des Betriebs auf. Offensichtlich ist das Arbeiten mit nicht nativen Daten (16 Bit) in dieser Architektur zeitlich erheblich teurer als mit nativen (32 Bit). Ich habe keine gründliche Studie durchgeführt, aber die Tatsache hat stattgefunden und ich sollte sie melden, um Missverständnisse zu vermeiden.

Das ist aber noch nicht alles. Da wir bereits den Weg der Trennung und Herrschaft eingeschlagen haben, gehen Sie doch weiter - extrahieren Sie die Wurzel Schritt für Schritt aus den Bits, beginnend mit den ältesten (in unserem Fall ist es kontraproduktiv, mit den jüngeren zu beginnen). Das Algorithmusschema ist das gleiche - wir fügen den nächsten Teil der Bits zum aktuellen Ergebnis hinzu und versuchen, das nächste Bit zum Ergebnis hinzuzufügen, um zu überprüfen, ob wir den Wurzelwert überschritten haben. Die Besonderheit ist, dass wir nur die hohen Bits des Arguments überprüfen können, bis wir zu den niedrigen Bits gelangen.

Bei der Implementierung verwenden wir einen weiteren Trick: Anstatt unsere subtrahierten Zahlen nach rechts zu verschieben, verschieben wir unser dekrementiertes Argument nach links, die Bedeutung ändert sich nicht und die Geschwindigkeit steigt. Es nimmt aufgrund von zwei Faktoren zu - 1) es reicht aus, nur 16-Bit-Zahlen zu subtrahieren (es gibt eine Besonderheit, und es muss berücksichtigt werden, aber wir erwägen eine Fallstudie, vout) und 2) wir müssen das Quadrat des nächsten Bits nicht verschieben, da dies immer der Fall ist gleich eins. Aber Sie müssen für alles auf dieser Welt bezahlen und wir werden die erweiterte Differenz (6 Bytes) nach links und um 2 Bits pro Takt verschieben. Wir betrachten den Pseudocode

  1. Anfangswerte -> n = 0; H1 = 0;
  2. 16 mal wiederholen -> (H1, H) << 2; T = H1-n-1; wenn (T> 0) {H1 = T; n = n + 2}; n << 1;

und bewerten Sie die Ausführungszeit, wobei Sie 16 * (12 (erweiterte Schicht) +4 (Berechnung der Differenz) +1 (Lösung) +2 (Zuweisung) +1 (Erhöhung) +2 (Verschiebung)) = 16 * 22 = 352 Maßnahmen erhalten Das Ergebnis ist nahezu perfekt. Bei der Implementierung dieser Option gibt es kleine Fallstricke. Ich überlasse dies erneut dem neugierigen Leser (nun, er bekommt den Job).

Nun, zum Abschluss des Abschnitts, der mich dazu veranlasste, diesen Beitrag zu schreiben. Es gibt eine wundervolle McuCpp-Bibliothek, die von Anton Chizhov verfasst wurde und in der Andriescu, basierend auf der Loki-Klasse der Autorenschaft, ungewöhnlich elegant ist (soweit Eleganz auf C ++ - Vorlagen angewendet werden kann). Arbeiten Sie mit Pins <a « github.com/KonstantinChizhov/ Mcucpp »Ich habe großen Respekt vor dem genannten Autor (beide) und habe kürzlich im Zusammenhang mit den Umständen, auf die ich später noch eingehen werde, die Quellen dieser Bibliothek angeschaut und erneut bewundert.

Unter anderen Dateien sah ich jedoch template_utils.h, in der einige Hilfsroutinen implementiert waren, und unter ihnen eine ganzzahlige Wurzel einer 32-Bit-Zahl. Die Tatsache, dass der einfachste sequentielle Approximationsalgorithmus mit Multiplikation verwendet wird, ist nicht beängstigend, da dieser Algorithmus nicht so viel an Geschwindigkeit verliert, aber an Verständlichkeit viele Punkte voraus gibt und trotzdem gewinnt. Aber die Tatsache, dass es etwas ungenau implementiert wurde (in Bezug auf die Leistung), hat mir nicht wirklich gefallen, weil "Kinder es sehen können". Ungenauigkeit besteht darin, die ausgewählte Zahl mit 32 Bit darzustellen, da wir sicher wissen, dass die Wurzel der 32-Bit-Zahl nicht über 16 Bit hinausgeht. Warum müssen wir also null Bytes verschieben? Und genau dies ist der Fall, wenn der Compiler selbst niemals eine Optimierung erraten wird und dabei helfen sollte.

Offensichtliche Funktionskonvertierung

 static inline uint32_t sqrt(uint32_t value) { uint16_t result = 0; uint16_t add = 0x8000; for (uint8_t i = 16; i !=0; ++i) { uint32_t rootGuess = result | add; uint32_t guess = rootGuess * rootGuess; if (value >= guess) { result = rootGuess; } add >>= 1; } return result; } 

ermöglicht es uns, 2 Zyklen bei einer Bitverschiebung und 2 Zyklen bei der Erstellung des nächsten Faktors für jeden Zyklus zu speichern, und die Organisation des Zyklus in der angegebenen Form umfasst weitere 4 Zyklen (ich weiß, dass der Compiler eine solche Optimierung für uns durchführen kann, aber warum nicht explizit helfen? ), was sehr gut für rein kosmetische Codeänderungen geeignet ist, die die Verständlichkeit nicht im geringsten beeinträchtigen.

Späterer Hinweis - ein Kommentar ließ mich denken, dass es korrekter wäre

  for (uint_fast8_t i= ...) 

Danke Oleg für die Hilfe.

Die Kirsche auf dem Kuchen ist die Funktion des Extrahierens der gesamten Quadratwurzel aus der Zeichennummer direkt darunter, die behauptet, √-1 = 65635 = -1 zu sein. Warum nicht, was schlimmer ist als jedes andere Ergebnis, ist dies für uns keine Ausnahme Ursache in MK, und die ganze Quadratwurzel einer negativen Zahl existiert nicht.

Nun, die Schlussfolgerung, warum ich mich an die Bibliothek von Anton Chizhov gewandt habe. Ich wurde von einem kürzlich veröffentlichten Beitrag zum inländischen RTOS für MK unter dem Namen MAX (MultiAgent Coherent System) aufgefordert - siehe das Epigraph zu dem von seinen Erstellern beworbenen Beitrag, der auf MK portiert wurde, das von Milander produziert wurde. Hinweis - Dieser Beitrag ist keineswegs Werbematerial und wird den Lesern bald klar. Von den oben genannten mcucpp-Autoren des Betriebssystems wurde die Implementierung eines Ringpuffers verwendet (ohne die Vorteile der Anton-Bibliothek zu beeinträchtigen, muss ich sagen, dass dieser Teil keine Referenz ist und dies immer noch eine weiche Formulierung ist, über die ich in einem anderen Beitrag geschrieben habe, den ich überhaupt nicht veröffentlichen werde). Da ich eng mit den Produktionsstätten von Milander zusammenarbeite, interessierte mich das Material und ich folgte dem Link zur Entwickler-Website.

Hier beginnt der nächste Schrei Jaroslawnas.

Letztes Jahr, als die Erstellung des inländischen RTOS zum ersten Mal angekündigt wurde, habe ich eine Beschreibung des Softwareprodukts von dieser Website heruntergeladen, aber irgendwie haben meine Hände die Studie nicht erreicht. Aufgrund meiner Tätigkeit muss ich mich mit Haushaltskomponenten befassen (ich verstehe genug ...), daher wäre es schön, die entsprechende Software zu haben. Als ich mich daran erinnerte, wie der Direktor des Unternehmens in der Veröffentlichung des letzten Jahres über die Millionen Rubel sprach, die für die Entwicklung ausgegeben wurden, und über das große Team, das an der Entwicklung dieses Softwareprodukts arbeitete, entschied ich mich, die Testversion zum kostenlosen Download zur Verfügung zu stellen, und hier teile ich die Ergebnisse.

Zunächst hat sich das Volumen der Beschreibung für ein halbes Jahr fast halbiert (von 115 auf 55 Seiten), und wenn das Verschwinden von Anwendungen mit Screenshots, die den Prozess der Einführung dritter Produkte aus der „Programmbeschreibung“ beschreiben, zu begrüßen ist, dann nicht das Erscheinungsbild dieser Materialien (deren Erstellung) Ich habe, obwohl nicht sehr bedeutsam, aber immer noch Zeit und Geld) in einem Dokument wie „Operator's Guide“ verbracht. Ich persönlich bin ratlos. Darüber hinaus sehen wir im allerersten Satz des Dokuments eine deutliche Abweichung von der Wahrheit, da RTOS selbst in keiner Weise „Programme erstellen“ soll. Aus irgendeinem Grund haben sich die Autoren solche Aussagen in der vorherigen Version des Dokuments nicht erlaubt. Der Einfluss des Marketingdienstes ist zu spüren. Es liefert auch, dass, wenn sich die Beschreibung früher im Ordner / docs des Stammverzeichnisses befand und dies logisch war, sie jetzt in / toolchain / macs / docs versteckt ist. Nun, wie sie in meiner Jugend sagten: "Jeder ist auf seine Weise verrückt", fahren wir fort.

Ich beginne mit der Beschreibung, dem Quellcode (der freundlicherweise in der Testversion enthalten ist) und finde verwirrt, dass keine Treiber für Peripheriegeräte vorhanden sind, die für die Arbeit mit diesem Betriebssystem geeignet sind. Zuerst habe ich vorgeschlagen, dass dies eine Funktion der Testversion ist, dann im Forum in den Informationen der Entwickler finde ich, dass es wirklich keine Treiber gibt, aber sie arbeiten daran. Mehr als sechs Monate (sechs Monate, Carl, eigentlich fast ein Jahr) ab dem Zeitpunkt, an dem das Betriebssystem für MK veröffentlicht wurde, und sie arbeiten an Treibern. Natürlich oder wie gesagt, es versteht sich von selbst, dass von keinem dritten Produkt (Dateisystem, Netzwerkstapel, USB-Stack) die Rede sein kann. Eine lustige Idee der Autoren über die Anforderungen an die Softwareentwicklung für MK, okay, fuhr wieder.

Das heißt, das deklarierte Betriebssystem, dessen hervorgehobenes Merkmal die Organisation der Interaktion innerhalb eines Systems mit mehreren Controllern ist, verfügt nicht über native Mittel zum Organisieren dieser Interaktion. Was wir unter dem Strich haben - und wir haben Aufgabenverwaltung, eigentlich einen Schuppen, minimalen Zeitaufwand und Mittel zum Synchronisieren von Aufgaben, und das ist alles - gelinde gesagt lustig. Okay, wir werden weiter schauen, auch in einem solchen Satz von Komponenten sind interessante Lösungen möglich, insbesondere wenn Sie bedenken, dass ich an einem Standort (nicht in der Firma des Herstellers) eine „Prüfung“ des Quellcodes dieses Betriebssystems durch Bezugnahme gesehen habe. In diesem Dokument heißt es, dass das Softwareprodukt keine Komponenten von Drittanbietern (Import) verwendet und original ist. Dies muss unbedingt sichergestellt werden.

Die erste Beobachtung ist, dass, wenn Sie Original-ARM-Dateien verwenden, die im Quellcode-Paket enthalten sind, um auf eine bestimmte Cortex-M0-Architektur (1986 BE1T) zu portieren, dies der Verwendung von (importierten) Textfragmenten von Drittanbietern sehr ähnlich ist - ich persönlich denke, dass dies die Verwendung ist, aber Ich weiß wahrscheinlich nicht alles. Nun, und zweitens ist der Quellcode des Shedulers und der zugehörigen Task-Management-Komponenten wirklich originell und enthält keine Analoga (zumindest kenne ich keine), aber dies ist die Art von Originalität, wenn ich mich an den Satz des alten Schamanen aus dem Film "Der böse Geist von Yambuya" erinnere der große Jäger: "Schneiden Sie die Ohren ab, kochen Sie und essen Sie - hätten Sie geraten?"

Ich werde versuchen zu erklären, dass beim Entwurf des Betriebssystems im Allgemeinen und des RTOS im Besonderen eines der schwierigen Probleme darin besteht, den Zugriff aller Prozesse im System auf eine gemeinsam genutzte Ressource - Prozessor-Laufzeit - sicherzustellen. , ( ) , . ( , , MPU), .

, , , , . (1) , , FREE-RTOS 20 , ( , , ).

, , 60 ( ). , . ( ) , (, ) ,

  1. (n)
  2. — , 20*(3*4)=240 . , , , .

, , ( , , ) . , ( , ). mcucpp ( — ), .

— - , .

(, , ) - — . ( , , ), , ( 2013) 1 , 2019 .

, :

  1. ( , ) ( , , , , ),
  2. ( ),
  3. () 2,
  4. HAL, CMSIS (- ),
  5. ,
  6. ,
  7. (3rd part), ,
  8. ,
  9. ,
  10. , (, , ..) « »,
  11. , , ( , , MIT , « »), , (?).

, , , 5 ( , , 10, IDE). , , .

, , , .

, , () .

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


All Articles