
Einführung
Ich wollte schon lange die Technik des Programmierens von UDB-Blöcken in Cypress PSoC-Controllern lernen, aber irgendwie haben alle meine Hände nicht erreicht. Und so entstand ein Problem, bei dem dies getan werden konnte. Als ich die Materialien aus dem Netzwerk verstand, stellte ich fest, dass praktische Empfehlungen für die Arbeit mit UDB auf verschiedene Variationen von Zählern und PWMs beschränkt sind. Aus irgendeinem Grund nehmen alle Autoren ihre Variationen dieser beiden kanonischen Beispiele vor, so dass die Beschreibung von etwas anderem für die Leser durchaus interessant sein kann.
Also. Es gab ein Problem bei der dynamischen Verwaltung einer langen Reihe von WS2812B-RGB-LEDs. Klassische Ansätze zu diesem Thema sind bekannt. Sie können das triviale Arduino nehmen, aber dort erfolgt die Ausgabe programmgesteuert. Während die Daten ausgegeben werden, ist alles andere inaktiv, da sonst die Zeitdiagramme fehlschlagen. Sie können STM32 verwenden und Daten entweder über DMA an PWM oder über DMA an SPI ausgeben. Techniken sind bekannt. Ich habe sogar einmal persönlich eine Reihe von 16 Dioden über SPI gesteuert. Aber der Overhead ist großartig. Ein Datenbit in LEDs belegt 8 Bit im Speicher für PWM und 3 bis 4 Bit (abhängig von der PLL-Kühle in der Steuerung) für SPI. Es gibt zwar nur wenige LEDs, dies ist jedoch nicht beängstigend. Wenn jedoch beispielsweise einige Hundert vorhanden sind, sollten 200 * 24 = 4800 Bit = 600 Byte nützliche Daten physisch in einem Puffer mit einer Kapazität von mehr als 4 Kilobyte für die PWM-Option oder mehr als 2 Kilobyte für SPI gespeichert werden Optionen. Für die dynamische Anzeige von Puffern sollten mehrere vorhanden sein, und STM32F103 verfügt über RAM für alles, was mit 20 Kilobyte zu tun hat. Nicht dass wir auf eine nicht realisierbare Aufgabe gestoßen wären, aber ein Grund zu prüfen, ob dies auf dem PSoC implementiert werden kann, ohne zusätzlichen RAM ausgeben zu müssen, ist ziemlich wichtig.
Theorie Referenzen
Lassen Sie uns zunächst herausfinden, was für ein Biest eine solche UDB ist und wie sie damit arbeiten. Dabei helfen wunderbare Lehrfilme des Controller-Herstellers.
Sie sollten
von hier aus mit dem Anschauen beginnen. Am Ende jedes Videos befindet sich ein Link zur nächsten Serie. Schritt für Schritt erwerben Sie Grundkenntnisse und betrachten das kanonische Beispiel „Zähler“. Nun, und ein Ampelkontrollsystem.
Etwa das gleiche, aber in kleine Stücke geschnitten, können
Sie hier sehen . Mein Video wurde nicht abgespielt, kann aber heruntergeladen und lokal angezeigt werden. Unter anderem gibt es auch ein kanonisches Beispiel für die Implementierung von PWM.
Fertige Lösungen
Um das Rad nicht neu zu erfinden (und umgekehrt - um die Methodik aus den Erfahrungen anderer zu lernen), stöberte ich im Netzwerk nach vorgefertigten Lösungen für die Steuerung von RGB-LEDs. Die beliebteste Lösung ist StripLightLib.cylib. Aber seit vielen Jahren plant er, Add DMA-Unterstützung hinzuzufügen. Aber ich möchte eine Lösung ausprobieren, die nicht vom Zentralprozessor abhängt. Ich möchte den Prozess starten und ihn vergessen und mich auf die Vorbereitung des nächsten Frames konzentrieren.
Die Lösung, die meinen Wünschen entspricht, wurde unter
https://github.com/PolyVinalDistillate/PSoC_DMA_NeoPixel gefunden .
Dort ist alles in UDB implementiert (aber LEDs sind nur eine Ausrede, das Ziel ist es, UDB zu lernen). Es gibt Unterstützung für DMA. Und das Projekt dort ist klar schön organisiert.
Probleme der als Basis gewählten Lösung
Wie ist die "Firmware" im Projekt PSoC_DMA_NeoPixel, kann jeder nach dem Lesen des Artikels sehen. Dadurch wird das Material repariert. Im Moment möchte ich nur sagen, dass ich zuerst die Logik der ursprünglichen Firmware vereinfacht habe, ohne den Ressourcenverbrauch zu reduzieren (aber es ist einfacher zu verstehen). Dann begann er zu experimentieren, um die Automatenlogik zu ersetzen, die einen Ressourcengewinn versprach, aber auf ein ernstes Problem stieß. Und so entschied er - es wird nicht beseitigt! Und vage Zweifel quälten mich: Hatte der englische Autor das gleiche Problem? Seine Demo blinkt sehr schön mit LEDs. Aber was ist, wenn wir die schöne Füllung durch „alle Einheiten“ ersetzen und die Ausgabe nicht mit unseren Augen, sondern mit einem Oszilloskop steuern?
So grob wie möglich (man könnte sogar „brutal“ sagen) bilden wir die Daten:
memset (pPixelArray,0xff,sizeof(pPixelArray));
Und hier sehen wir ein solches Bild auf einem Oszilloskop:

Das erste Bit hat eine andere Breite als der Rest. Ich bat darum, alle Einheiten zu schicken, aber nicht alle gehen. Unter ihnen auf Null gesetzt! Ändern Sie den Scan:

Die Breite ist für jedes achte Bit unterschiedlich.
Im Allgemeinen ist dieses Beispiel als eigenständige Lösung nicht geeignet, sondern als Inspirationsquelle - einfach perfekt. Erstens ist seine Inoperabilität mit dem Auge nicht sichtbar (die LEDs sind immer noch hell, das Auge sieht nicht, dass sie halb halb leuchten), aber der Code ist gut strukturiert, es ist schön, ihn als Grundlage zu nehmen. Zweitens bietet dieses Beispiel Raum für Möglichkeiten zur Vereinfachung, und drittens werden Sie darüber nachdenken, wie Sie den Fehler beheben können. Das Wichtigste ist, das Material zu verstehen! Nachdem ich den Artikel gelesen habe, empfehle ich erneut, das ursprüngliche Beispiel zu analysieren und zu erkennen, wie es funktioniert.
Praktischer Teil
Jetzt fangen wir an zu üben. Wir testen die Hauptaspekte der Firmware-Entwicklung für UDB. Betrachten Sie die Beziehung und die grundlegenden Techniken. Öffnen Sie dazu
meine Version des Projekts . Der linke Block speichert Informationen zu Arbeitsdateien. Standardmäßig ist die Registerkarte
Quelle geöffnet. Die Hauptquelle des Projekts ist die Datei
main.c. Tatsächlich gibt es keine anderen Arbeitsdateien in der Gruppe
Quelldateien .

Die Gruppe
Generierte Quelle enthält Bibliotheksfunktionen. Es ist besser, sie nicht zu bearbeiten. Nach jeder Änderung der „Firmware“ von UDB wird diese Gruppe neu generiert. Wo ist also die Beschreibung des Codes für UDB in dieser Idylle? Um es zu sehen, müssen Sie zur Registerkarte
Komponenten wechseln:

Der Autor des ursprünglichen Projekts hat einen zweistufigen Satz von Komponenten erstellt. Auf der obersten Ebene liegt die Schaltung
NeoPixel_v1_2.cysch . Dies ist aus dem Hauptschema ersichtlich:

Die Komponente ist wie folgt:

Die Softwareunterstützung für dieses Schema wird später erläutert.
Stellen Sie in der Zwischenzeit fest, dass es sich selbst um eine reguläre DMA-Einheit und ein bestimmtes Symbol
NeoPixDrv_v1 handelt . Dieser mysteriöse Block ist oben im Baum beschrieben, der sich aus dem folgenden Tooltip ergibt:

UDB "Firmware"
Öffnen Sie diese Komponente (Datei mit der Erweiterung
.cyudb ). Die geöffnete Zeichnung ist einfach riesig. Wir beginnen zu verstehen, was was ist.

Im Gegensatz zum Autor des ursprünglichen Projekts betrachte ich die Übertragung jedes Datenbits in Form von drei gleichen (zeitlichen) Teilen:
- Startteil (immer 1)
- Datenteil
- Teil stoppen (immer 0)
Bei diesem Ansatz ist keine große Anzahl von Zählern erforderlich (im Original gab es bis zu drei Teile, die eine große Menge an Ressourcen verbrauchten). Die Dauer aller Teile ist gleich und kann mit einem Register eingestellt werden. Somit enthält der Übergangsgraph der Firmware die folgenden Zustände:
Leerlaufzustand . Die Maschine bleibt darin, bis neue Daten im FIFO eintreffen.

Aus den Schulungsvideos war mir nicht ganz klar, wie der Zustand der Maschine mit ALU zusammenhängt. Die Autoren verwenden Kommunikation selbstverständlich, aber ich als Anfänger konnte sie nicht sofort sehen. Lassen Sie uns einen kurzen Blick darauf werfen. Die obige Abbildung zeigt, dass der Ruhezustand mit dem Wert 1'b0 codiert ist. 3'b000 wird korrekter sein, aber der Editor wird alles trotzdem wiederholen. Die Eingänge des
Datenpfadblocks werden folgendermaßen beschrieben:

Wenn Sie darauf doppelklicken, wird eine detailliertere Version angezeigt:

Dies bedeutet, dass das Nullbit der Adresse des ALU-Befehls dem Nullbit der Variablen entspricht, die den Zustand der Maschine festlegt. Der erste ist der erste, der zweite ist der zweite. Falls gewünscht, können beliebige Variablen und sogar Ausdrücke mit den Adressbits des ALU-Befehls abgeglichen werden (in der Originalversion wurde das zweite Bit der Adresse des ALU-Befehls mit einem Ausdruck abgeglichen, außerdem wird es in der aktuellen Version nicht explizit verwendet, aber es ist sehr offensichtlich als gehirntragendes Beispiel, dann können Sie einen Blick darauf werfen).
Also. Bei den aktuellen Einstellungen der Eingänge, bei denen es sich um den binären Statuscode der Maschine handelt, wird ein solcher ALU-Befehl verwendet. Wenn wir uns im
Ruhezustand mit dem Code 000 befinden, wird der Nullbefehl verwendet. Da ist sie:

Ich weiß bereits aus diesem Eintrag, dass dies ein banaler NOP ist. Sie können jedoch darauf doppelklicken und die Vollversion lesen:

NOPs sind überall eingeschrieben. Register sind mit nichts gefüllt.
Lassen Sie uns nun herausfinden, was für eine mysteriöse Flagge
! NoData , die die Maschine zwingt, den
Ruhezustand zu verlassen. Dies ist der Ausgang aus dem
Datenpfadblock . Insgesamt können bis zu sechs Ausgänge beschrieben werden. Es ist nur so, dass
Datapath viel mehr Flags erzeugen kann, aber es gibt nicht genügend Trace-Ressourcen für alle, sodass wir auswählen müssen, welche sechs (oder weniger) wir wirklich benötigen. Hier ist die Liste in der Abbildung:

Wenn Sie darauf doppelklicken, werden die Details angezeigt:

Hier ist die vollständige Liste der Flags, die angezeigt werden könnten:

Nachdem Sie das gewünschte Flag ausgewählt haben, sollten Sie ihm einen Namen geben. Von nun an hat das System eine Flagge. Wie Sie sehen können, ist das
NoData- Flag der Name für den
Blockstatus der Kette
F0 (leer) . Das heißt, ein Zeichen dafür, dass sich keine Daten im Eingabepuffer befinden. Ah
! NoData bzw. seine Inversion. Zeichen der Datenverfügbarkeit. Sobald die Daten in das FIFO eintreten (programmgesteuert oder unter Verwendung von DMA), wird das Flag gelöscht (und seine Inversion gespannt), und beim nächsten Taktzyklus verlässt der Automat den Ruhezustand und wechselt in den
GetData- Zustand.

Wie Sie sehen können, verlässt der Automat diesen Zustand bedingungslos, nachdem er genau einen Taktzyklus ausgeführt hat. Für diesen Status werden im Übergangsdiagramm keine Aktionen angezeigt. Aber Sie sollten immer darauf achten, was ALU tun wird. Der Statuscode lautet 1'b1, dh 3'b001. Wir sehen uns die entsprechende Adresse in ALU an:

Da ist etwas. Wenn Sie keine Erfahrung mit dem Lesen der hier geschriebenen Informationen haben, öffnen Sie sie, indem Sie auf die entsprechende Zelle doppelklicken:

Daraus folgt, dass die ALU selbst noch keine Aktionen ausführt. Der Inhalt von FIFO0, dh die vom Programm oder vom DMA-Block kommenden Daten, werden jedoch in das A0-Register gestellt. Mit Blick auf die Zukunft werde ich sagen, dass A0 als Schieberegister verwendet wird, aus dem das Byte in serieller Form austritt. Register A1 setzt den Wert von Register D1. Im Allgemeinen werden normalerweise alle D-Register in Software gefüllt, bevor die Hardware aktiv wird. Wenn wir dann die API untersuchen, werden wir sehen, dass die Anzahl der Takt-Ticks in dieses Register eingefügt wird, wodurch die Dauer des dritten Bits festgelegt wird. Also. In A0 fiel der verschobene Wert und in A1 die Dauer des Startteils des Bits. Und beim nächsten Schlag wird die Maschine sicherlich in den Zustand
Constant1 gehen .

Wie der Name des Zustands andeutet, wird hier die Konstante 1 generiert. Schauen wir uns die Dokumentation für die LED an. So sollte das Gerät übertragen werden:

Und hier ist es - Null:

Rote Linien habe ich hinzugefügt. Wenn wir davon ausgehen, dass die Dauer von Dritteln gleich ist, sind die Anforderungen für die Dauer von Impulsen (in derselben Dokumentation angegeben) erfüllt. Das heißt, jeder Impuls besteht aus einer Starteinheit, einem Datenbit und einer Stoppnull. Tatsächlich wird die Starteinheit übertragen, wenn sich die Maschine im Zustand
Constant1 befindet .
In diesem Zustand rastet die Maschine das Gerät in seinem internen Auslöser ein. Der Name des Triggers lautet
CurrentBit . Im ursprünglichen Projekt war es im Allgemeinen ein Auslöser, der den Zustand des Hilfsautomaten festlegt. Ich entschied, dass diese Maschine nur alle verwirren würde, also startete ich einfach einen Auslöser. Es wird nirgendwo beschrieben. Wenn Sie jedoch die Statuseigenschaften eingeben, wird der folgende Datensatz in der Tabelle angezeigt:

Und unter dem Status in der Grafik befindet sich ein solcher Text:

Lassen Sie sich nicht durch das Gleichheitszeichen symbolisieren. Dies sind die Funktionen des Editors. Im resultierenden Verilog-Code (automatisch vom selben System generiert) wird ein Pfeil angezeigt:
Constant1 : begin CurrentBit <= (1); if (( CycleTimeout ) == 1'b1) begin MainState <= Setup1 ; end end
Der in diesem Trigger zwischengespeicherte Wert ist die Ausgabe unseres gesamten Blocks:

Das heißt, wenn die Maschine in den Zustand von
Konstante1 eingetreten ist, erhält die Ausgabe des Blocks, den wir entwickeln, einen. Nun wollen wir sehen, wie die ALU für die Adresse 3'b010 programmiert ist:

Wir enthüllen dieses Element:

Einheit 1 wird vom Register A1 abgezogen. Der Ausgabewert von ALU fällt in das Register A1. Oben haben wir angenommen, dass A1 ein Taktzähler ist, der zum Einstellen der Dauer des Ausgangsimpulses verwendet wird. Ich möchte Sie daran erinnern, dass es im letzten Schritt von D1 gebootet wurde.
Was ist die Voraussetzung für den Austritt aus einem Staat?
CycleTimeOut . Es wird unter den Ausgängen wie folgt beschrieben:

Also bringen wir Logik zusammen. Im vorherigen Zustand fiel der Inhalt des zuvor vom Programm ausgefüllten Registers D1 in das Register A1. In diesem Schritt übersetzt die Maschine den
CurrentBit- Trigger in eins, und in ALU nimmt das A1-Register bei jedem Taktzyklus ab. Wenn A1 Null wird, wird automatisch das Flag gesetzt, dem der Autor den Namen
CycleTimeout gegeben hat , wodurch der Computer in den
Status Setup1 wechselt .
Der Zustand
Setup1 bereitet Daten für die Übertragung des
Nutzimpulses vor.

Wir sehen uns die ALU-Anweisung bei 3'b011 an. Ich werde es sofort öffnen:

Es scheint, dass ALU keine Aktionen hat. Operation NOP. Und der ALU-Ausgang kommt nicht weiter. Aber das ist nicht so. Eine äußerst wichtige Aktion ist die Datenverschiebung in ALU. Tatsache ist, dass das Übertragsbit zwischen den Ausgängen mit unserer
ShiftOut- Kette verbunden ist:

Und als Ergebnis dieser Verschiebungsoperation wird der verschobene Wert selbst nirgendwo hinkommen, aber die
ShiftOut- Kette nimmt den Wert des höchstwertigen Bits des Registers A0 an. Das heißt, die Daten, die übertragen werden sollen. Im Status des Diagramms ist ersichtlich, dass dieser Wert, der die ALU in der
ShiftOut- Kette
belassen hat, im
CurrentBit- Trigger zwischengespeichert wird. Lassen Sie mich die Zeichnung noch einmal zeigen, um den Artikel nicht zurückzuspulen:

Die Übertragung des zweiten Teils des Bits beginnt - der unmittelbare Wert ist 0 oder 1.
Wir kehren zu den Anweisungen für ALU zurück. Zusätzlich zu dem, was bereits gesagt wurde, ist es klar, dass der Inhalt des Registers D1 erneut in das Register A1 gestellt wird, um die Dauer des zweiten Drittels des Impulses erneut zu messen.
Der
DataStage- Status ist dem
Constant1- Status sehr ähnlich. Der Automat subtrahiert einfach eins von A1 und tritt in den nächsten Zustand ein, wenn er Null erreicht. Lassen Sie es mich sogar so zeigen:

und so:

Dann kommt der Zustand von
Setup2 , dessen Essenz wir bereits kennen.

In diesem Zustand wird der
CurrentBit- Trigger auf Null zurückgesetzt (da das dritte Drittel des Impulses übertragen wird, der
Stoppteil , und es ist immer Null). ALU lädt den Inhalt von D1 in A1. Sie können es sogar in einer kurzen Notiz mit Ihrem geschulten Auge sehen:

Der Status von
Constant0 ist vollständig identisch mit dem
Status von
Constant1 und
DataStage . Subtrahieren Sie die Einheit von A1. Wenn der Wert Null erreicht,
kehren Sie zum
ShiftData- Status zurück:


Der Status von
ShiftData ist komplexer. In den entsprechenden Anweisungen für ALU werden die folgenden Aktionen ausgeführt:

Das Register A0 wird um 1 Bit verschoben, und die Ergebnisse werden in A0 zurückgesetzt. In A1 wird der Inhalt von D1 erneut eingefügt, um mit dem Messen des Startdrittels für das nächste Datenbit zu beginnen.
Es ist besser, die Ausgabepfeile unter Berücksichtigung der Prioritäten zu berücksichtigen, für die wir auf den
ShiftData- Status
doppelklicken .

Wenn nicht das letzte Bit übertragen wird (etwa wie dieses Flag gebildet wird, etwas niedriger), übertragen wir eines für das nächste Bit des aktuellen Bytes.
Wenn das letzte Bit übertragen wird und keine Daten im FIFO vorhanden sind, wechseln wir in den Ruhezustand.
Wenn schließlich das letzte Bit übertragen wird, sich jedoch Daten im FIFO befinden, fahren wir mit der Auswahl und Übertragung des nächsten Bytes fort.
Nun zum Bitzähler. In ALU befinden sich nur zwei Batterien: A0 und A1. Sie sind bereits vom Schieberegister bzw. vom Verzögerungszähler belegt. Daher wird ein Bitzähler extern verwendet.

Doppelklicken Sie darauf:

Der Wert beim Booten ist sechs. Es wird mit dem im Abschnitt "Variablen" beschriebenen
LoadCounter- Flag geladen:

Das heißt, wenn das nächste Datenbyte genommen wird, wird diese Konstante auf dem Weg geladen.
Wenn die Maschine in den
ShiftData- Status wechselt, verringert der Zähler den Wert. Wenn es Null erreicht, ist der Ausgang
TerminalCount mit der Schaltung unseres
FinalBit-Seeds verbunden . Es ist diese Schaltung, die festlegt, ob die Maschine das nächste Bit des aktuellen Bytes überträgt oder ein neues Byte überträgt (nun, oder auf ein neues Datenpaket warten).
Eigentlich ist alles aus Logik. Wie das
SpaceForData- Signal
erzeugt wird , das den Status des
Hungry- Ausgangs
festlegt (wodurch die DMA-Einheit darüber informiert wird, dass die nächsten Daten übertragen werden können), werden die Leser aufgefordert, unabhängig zu verfolgen.
Software-Support
Der Autor des ursprünglichen Projekts hat sich dafür entschieden, Software-Unterstützung für das gesamte System in dem Block bereitzustellen, der die integrierte Lösung beschreibt. Ich möchte Sie daran erinnern, dass es sich um diesen Block handelt:

Ab dieser Ebene können Sie sowohl die DMA-Bibliothekseinheit als auch alle im UDB-Teil enthaltenen Teile steuern. Um die API zu implementieren, fügte der Autor des Originals die Header- und Programmdateien hinzu:

Das Textformat dieser Dateien macht Sie traurig. Die ganze Schuld liegt in der Liebe der PSoC Designer-Entwickler zu den "reinen". Daher die schrecklichen Makros und Kilometernamen. Die Klassenorganisation in C ++ wäre hier nützlich. Zumindest haben wir dies bei der Implementierung unseres RTOS MAX überprüft: Es hat sich als schön und bequem herausgestellt. Aber hier können Sie viel streiten, aber Sie müssen das verwenden, was von oben auf uns herabgesetzt wurde. Ich werde nur kurz zeigen, wie die API-Funktion mit diesen Makros aussieht:
volatile void* `$INSTANCE_NAME`_Start(unsigned int nNumberOfNeopixels, void* pBuffer, double fSpeedMHz) {
Diese Spielregeln müssen akzeptiert werden. Jetzt wissen Sie, woher Sie sich bei der Entwicklung Ihrer Funktionen inspirieren lassen können (am besten im ursprünglichen Projekt). Und ich spreche lieber über die Details und nehme die Option, die bereits vom Generator verarbeitet wurde.
Nach dem Generieren des Codes (unten beschrieben) wird diese Datei hier gespeichert:

Und die Ansicht ist bereits perfekt lesbar. Bisher gibt es zwei Funktionen. Der erste initialisiert das System, der zweite startet die Datenübertragung vom Puffer zur LED-Leitung.
Die Initialisierung betrifft alle Teile des Systems. Der 7-Bit-Zähler, der Teil des UDB-Systems ist, wird initialisiert:
NP_Neo_BITCNT_Start();
Es gibt eine konstante Berechnung, die in das D1-Register geladen werden sollte (ich erinnere mich, dass sie die Dauer jedes der dritten Bits festlegt):
unsigned char fCyclesOn = (unsigned char)(0.35/(1.0/(fSpeedMHz))); CY_SET_REG8(NP_Neo_DPTH_D1_PTR, fCyclesOn+1);
Das Einrichten eines DMA-Blocks übernimmt den größten Teil dieser Funktion. Der Puffer wird als Quelle und der FIFO0 des UDB-Blocks als Empfänger verwendet (NP_Neo_DPTH_F0_PTR im Kilometerdatensatz). Der Autor hatte einen Teil dieser Einstellung in der Datenübertragungsfunktion. Aber meiner Meinung nach ist es zu verschwenderisch, alle Berechnungen für jede Übertragung durchzuführen. Besonders wenn man bedenkt, dass eine der Aktionen innerhalb der Funktion sehr, sehr umfangreich aussieht.
Die zweite Funktion vor dem Hintergrund der ersten ist die Spitze des Lakonismus. Es ist nur so, dass der erste in der Initialisierungsphase aufgerufen wird, wenn die Leistungsanforderungen recht kostenlos sind. Während des Betriebs ist es besser, Prozessorzyklen nicht mit überflüssigen Dingen zu verschwenden:
void NP_Update() { if(NP_g_pFrameBuffer) { CyDmaChEnable(NP_g_nDMA_Chan, 1); } }
Es gibt eindeutig nicht genügend Funktionen, um mit mehreren Puffern zu arbeiten (um eine doppelte Pufferung bereitzustellen), aber im Allgemeinen würde eine Diskussion der API-Funktionen den Rahmen des Artikels sprengen. Die Hauptsache ist nun zu zeigen, wie man der entwickelten Firmware Software-Unterstützung hinzufügt. Jetzt wissen wir, wie es geht.
Projektgenerierung
Also, der gesamte Firmware-Teil ist fertig, die API wird hinzugefügt, was ist als nächstes zu tun? Wählen Sie den Menüpunkt
Build-> Generate Application .

Wenn alles gut geht, können Sie die Registerkarte
Ergebnisse öffnen und die Datei mit der Erweiterung
rpt anzeigen .

Es zeigt, wie viel Systemressourcen in die Implementierung der Firmware geflossen sind.


Wenn ich die Ergebnisse mit denen des ursprünglichen Projekts vergleiche, wird meine Seele wärmer.
Gehen Sie nun zur Registerkarte
Quelle und beginnen Sie mit der Arbeit mit dem Software-Teil. Dies ist jedoch bereits trivial und erfordert keine besonderen Erklärungen.

Fazit
Ich hoffe, dass die Leser anhand dieses Beispiels etwas Neues und Interessantes über die praktische Arbeit mit UDB-Blöcken gelernt haben. Ich habe versucht, mich auf eine bestimmte Aufgabe (LED-Steuerung) sowie auf die Entwurfsmethodik zu konzentrieren, da ich einige Aspekte verstehen musste, die für Spezialisten offensichtlich waren. Ich habe versucht, sie zu markieren, während die Erinnerungen an die Suche frisch sind. Was das gelöste Problem betrifft, so erwiesen sich die Zeitdiagramme für mich als nicht so ideal wie die des Autors der ursprünglichen Entwicklung, aber sie passen perfekt zu den in der Dokumentation für die LEDs definierten Toleranzen, und die Systemressourcen waren erheblich geringer.
Tatsächlich ist dies nur ein Teil der gefundenen nicht standardmäßigen Informationen. Insbesondere aus den meisten Materialien scheint es, dass UDB nur mit seriellen Daten gut funktioniert, dies ist jedoch nicht der Fall. Gefundene Anwendungsnotiz, die kurz zeigt, wie Sie Daten ansteuern und parallel schalten können. Basierend auf diesen Informationen könnten wir spezifische Beispiele betrachten (obwohl es unmöglich ist, den FX2LP, einen anderen Controller von Cypress, zu überschatten: PSoC hat eine niedrigere USB-Busgeschwindigkeit).
Mein Kopf dreht sich um Ideen, wie ich das Problem des „Flashens“ eines 3D-Druckers lösen kann, das mich schon lange gequält hat. Dort verschlingen Interrupts, die Schrittmotoren bedienen, nur einen wahnsinnigen Prozentsatz der CPU-Zeit. Im Allgemeinen habe ich in einem
Artikel über RTOS MAX viel über Interrupts und Prozessorzeit gesprochen. Es gibt Schätzungen, dass es für die Wartung von Schrittmotoren möglich ist, alle temporären Hütten vollständig zur UDB zu bringen, so dass der Prozessor eine rein rechnerische Aufgabe bleibt, ohne befürchten zu müssen, dass er in einem bestimmten Zeitfenster keine Zeit dafür hat.
Diese Dinge können jedoch nur begründet werden, wenn das Thema interessant ist.