Verwenden der UDB-PSoC-Controller von Cypress, um Unterbrechungen in einem 3D-Drucker zu reduzieren



In den Kommentaren zur Übersetzung der proprietären Dokumentation zu UDB wurde zu Recht darauf hingewiesen, dass einfach trockene Fakten nicht zum Verständnis des Materials beitragen. Aber dieses Dokument enthält genau die trockenen Fakten. Um sie mit Übung zu verdünnen, machen wir eine Pause von der Übersetzung. Lassen Sie uns diesen Block in unsere Hände legen und sehen, was und wie er in der Praxis erreicht werden kann.

Lange Einführung


Dieser Artikel ist der zweite Teil der konzipierten Trilogie. Hier befindet sich der erste Teil (RGB-LED-Steuerung über die Cypress UDB-Mikrocontroller-Einheit PSoC).

Zusätzlich zu den UDB-PSoC-Controllern von Cypress, auf denen bestimmte Schnittstellen implementiert sind, wäre es interessant zu prüfen, wie diese Blöcke Programmierern das Leben erleichtern können, indem der Zentralprozessor von bestimmten ressourcenintensiven Aufgaben entladen wird. Aber um zu klären, was ich tun werde, muss ich ein umfangreiches Vorwort schreiben.

Im Herbst 2015 kaufte ich einen brandneuen MZ3D 3D-Drucker und im Frühjahr 2016 hatte ich es satt, wie die Schrittmotoren klapperten. Die Zeiten waren wild, wir überlebten so gut wir konnten, also bestand die einzige Lösung darin, von Mikroschritt 1/16 auf 1/32 zu wechseln. Die Korrespondenz mit der Fabrik zeigte, dass dies bei Arduino nicht möglich ist. Wie sich herausstellte, gab es in der „Firmware“ jener Jahre eine Einschränkung: Bei einer Schrittfrequenz von mehr als 10 kHz wurden keine virtuellen Schritte ausgeführt, sondern zwei virtuelle Schritte, da das System sonst einfach nicht genügend Zeit hatte, um alle „Schritt“ -Interrupts zu verarbeiten. Es gab nur einen Ausweg - alles auf die ARM-Plattform zu ziehen. Es war ein Drag & Drop, kein Download, da es zu diesem Zeitpunkt auch keine vorgefertigten ARM-Lösungen gab. In ein paar Wochen habe ich das alles auf STM32F4 übertragen, das Motorengeräusch wurde angenehmer, das Problem wurde behoben.

Dann begann die Entwicklung des Betriebssystems in unserem Unternehmen, und bei den Besprechungen musste ich lange Zeit beweisen, dass der typische Ansatz zur Verarbeitung von Interrupts in Bezug auf die Geschwindigkeit nicht immer akzeptabel ist und nur diesen typischen, aber sehr gefräßigen Fall anspricht. Diskussionen zu diesem Thema werden in meinem Artikel über Interrupts im Betriebssystem hier veröffentlicht (Übersicht über ein russisches RTOS, Teil 8. Arbeiten mit Interrupts). Im Allgemeinen hat sich ein Problem in meinem Kopf schon lange gelöst: Häufige Hilfsinterrupts, die ein Subsystem bedienen, verlangsamen alles andere. Die einfache Verfeinerung des Zentralprozessors beseitigt natürlich das Problem, bringt aber nicht die tiefe moralische Zufriedenheit, dass alles richtig gemacht wird.

In regelmäßigen Abständen kam ich rein theoretisch auf diese Frage zurück. Zum Beispiel kam mir eines Tages der Gedanke, dass man anstelle eines teuren Controllers drei STM32F103C8T6 nehmen kann, bei denen ein fertiges Steckbrett unter Berücksichtigung der Lieferung 110 Rubel kostet und der Chip selbst noch billiger ist. In einem von ihnen soll nur die Motorsteuerungsfunktion herausgenommen werden. Lassen Sie ihn seine ganze Rechenleistung für diese Funktion aufwenden. Einige der anderen (vielleicht sogar eine) lösen andere Aufgaben (Verarbeiten von Befehlen, Arbeiten mit PWM, Aufrechterhalten der Temperatur usw.) in einer ruhigen Umgebung. Diese Lösung hat auch ein großes Plus: Die Gesamtzahl der Pins für mehrere Controller ist einfach riesig. Auf einem STM32 musste ich lange Zeit Solitaire auslegen, welchem ​​Bein ich zuordnen sollte. Obwohl die Schenkel der Timer-Ausgänge und die ADC-Schenkel der ARMs flexibler zugewiesen sind als die alten Controller (ein Ausgang der Hardwareeinheit kann einem von mehreren physischen Schenkeln zugewiesen werden), verstehen Sie, dass die Flexibilität möglicherweise nicht ausreicht, wenn Sie den Solitaire ausklappen. Wenn es viele Controller gibt, erhöht sich die Auswahl. Bei dem, der Schrittmotoren bedient, weisen wir im Allgemeinen einfach alle Beine als digitale Ausgänge zu. Die anderen haben auch die Möglichkeit, sich umzudrehen.

Ein Problem bei diesem Ansatz ist die Synchronisierung dieser Steuerungen. Theoretisch enthält das MAX Max RTOS alles, was Sie brauchen. Der Befehlshandler generiert eine Liste von Aufgaben zum Bewegen von Köpfen. In regelmäßigen Abständen ändert er sie (indem er Beschleunigungen mit neu angekommenen Aufgaben koordiniert). Daher sollte der Speicher für den Shaper und den Performer gemeinsam genutzt werden. RTOS MAX enthält die Funktionalität zum Organisieren eines solchen gemeinsam genutzten Speichers. Ich habe es hier beschrieben (Überblick über ein russisches RTOS, Teil 7. Mittel zum Datenaustausch zwischen Aufgaben). In der Praxis verdirbt jedoch eine Nuance alles: Die Wartung von Schrittmotoren ist eine zeitkritische Aufgabe. Die geringste Verzögerung, und wir erhalten Plastikflüsse für einen 3D-Drucker, für andere CNC-Maschinen - also zum Beispiel für Gewinde mit falschem Gewinde. Die Kommunikation über serielle Schnittstellen ist nicht die schnellste. Plus - Zeit für Schiedsverfahren und andere offizielle Bedürfnisse. Und es stellt sich heraus, dass alle Vorteile aus der Entfernung der Funktionalität vom Hauptprozessor in den Overhead gehen. Natürlich habe ich meine offizielle Position ausgenutzt: Ich habe dieses Problem mit den Entwicklern dieses Subsystems besprochen. Leider. Sie sagten, dass es eine Synchronisation ohne großen Aufwand im Betriebssystem gibt, aber für Geräte, die die entsprechenden Busse unterstützen. Wenn ich nun die TigerShark-Architektur als Grundlage nehme, organisiert das Betriebssystem alles für mich ohne Overhead. Nur die nach dieser Architektur hergestellten Controller sind um ein Vielfaches teurer als der gesamte 3D-Drucker, den ich einbauen wollte. Im Allgemeinen wieder inakzeptabel.

Wir nähern uns dem Ende einer langwierigen Einführung. Jemand wird sagen, dass ich aus irgendeinem Grund immer noch einen Prinzen auf einem weißen Pferd suche. Sie können alles ohne Betriebssystem nehmen und tun, und hier erwäge ich alle möglichen Optionen ... Sie können, Sie können, aber als das praktische Problem „Müde vom Abhören des Druckerabsturzes“ auftrat, wurde es schnell behoben. Das ist alles. Sie ist nicht mehr. Darüber hinaus gibt es seitdem neue Schrittmotortreiber, die dieses Problem im Allgemeinen auf völlig andere Weise lösen (sie erhalten einen Mikroschritt 1/16 und geben 1/256 aus). Und in dieser Einleitung beschreibe ich genau: "Es gibt keine schöne Lösung für das Problem häufiger Unterbrechungen." Eine hässliche Entscheidung wurde lange getroffen. Ich wollte keine Zeit damit verschwenden, andere hässliche Entscheidungen zu überprüfen. Sie haben nur in meinem Kopf gescrollt.

Aber als ich mich mit UDB-Blöcken beschäftigte, schien es mir, dass das Problem schön und dramatisch gelöst werden kann. Sie können einfach die Verarbeitung von Unterbrechungen von der Software auf die Firmware-Ebene übertragen, wobei der Computerteil dem Gewissen des Hauptprozessors überlassen bleibt. Keine zusätzlichen Controller erforderlich! Alles ist auf dem gleichen Chip platziert! Also, fangen wir an.

Sphärisches Pferd im luftleeren Raum


In diesem Artikel steht die Arbeit mit UDB selbst im Vordergrund. Wenn ich davon sprach, an eine bestimmte „Firmware“ gebunden zu sein, könnten sie mich zu Recht darauf hinweisen, dass ich mich mit dem Hub geirrt habe. Was ist das für GeekTimes? Daher ist UDB primär und Schrittmotoren sind nur eine schöne Sache zu veranschaulichen. In diesem Teil werde ich im Allgemeinen ein kugelförmiges Pferd in einem Vakuum machen. Er wird praktische Mängel haben, die ich im zweiten Teil beseitigen werde. Wenn Sie jedoch meine Aktionen wiederholen, können die Leser die Methode zur Entwicklung der Firmware für UDB beherrschen.

Also. Wie funktioniert der Schrittmotor-Steuermechanismus? Es gibt eine Aufgabe, die die Segmente, die der Kopf mit linearer Geschwindigkeit passieren muss, in eine Linie bringt. Bisher werde ich so tun, als würde ich mich nicht an die Beschleunigung am Anfang und Ende des Segments erinnern. Nur der Kopf sollte durchgehen. Neue Segmente werden in das Ende der Warteschlange gestellt. Basierend auf der Aufzeichnung vom Kopf sendet eine separate Aufgabe STEP- Signale an alle aktiven Motoren.

Lassen Sie den Drucker eine maximale Kopfgeschwindigkeit von 200 mm / s haben. Pro 1 Millimeter Bewegung seien 200 Schritte erforderlich (diese Zahl entspricht einem echten Drucker MZ3D-256C mit einem Mikroschritt 1/32). Dann müssen die Impulse mit einer Frequenz von bis zu 200 * 200 = 40.000 Hz = 40 kHz versorgt werden. Mit einer solchen Frequenz kann eine Aufgabe, die Schrittimpulse sendet, durchaus aufgerufen werden. Es muss programmgesteuert die Impulse selbst bilden und auch berechnen, wie lange nach dem nächsten Interrupt, der es aktiviert, aufgerufen werden soll.

Ich erinnere mich an einen Witz über Kolobok und die drei Bogatyrer, in dem Kolobok die Bogatyrer konsequent begrüßte, ihnen dann konsequent Fragen stellte und Antworten erhielt. Dann verabschiedete er sich nacheinander von ihnen. Nun, dann traf er sich mit den dreiunddreißig Rittern. Der Prozessor spielt die Rolle eines Brötchens, und die Schrittmotoren spielen die Rolle von Bogatyrs. Es ist klar, dass es bei Vorhandensein einer großen Anzahl von UDB-Blöcken möglich ist, die Arbeit mit Motoren zu parallelisieren, wobei jeder Motor an seinem Block gewartet wird. Und da wir Segmente haben, in denen sich die Motoren gleichmäßig bewegen, versuchen wir, die Ausrüstung bei solchen Transaktionen und nicht bei jedem Schritt zum Laufen zu bringen.

Welche Informationen benötigt ein kugelförmiges Pferd, um einen linearen Abschnitt im Vakuum zu durchlaufen?

  • Anzahl der Schritte.
  • Der Zeitraum zwischen den Schritten.

Zwei Parameter. UDB hat nur zwei Batterien und zwei Register der Parameter D0 und D1. Es scheint, dass alles realisierbar ist. Wir schätzen nur die Bittiefe, die diese Register haben sollten.

Erstens die Anzahl der Schritte. Wenn 8 Stellen vorhanden sind, kann der Drucker in einem Zyklus des UDB-Betriebs den Kopf des kartesischen Druckers um etwas mehr als 1 mm (200 Mikroschritte) bewegen. Nicht genug. Wenn die Kapazität 16 Bit beträgt, beträgt die Anzahl der Schritte 65536. Dies ist 65536/200 = 327 Millimeter. Akzeptabel für die meisten Modelle. Für Core, Delta und andere ist eine Schätzung erforderlich, aber insgesamt - für einen vollständigen Hub kann das Segment in mehrere Teile unterteilt werden. Es wird nicht so viele geben (zwei, also maximal drei).

Nun die Periode. Die Taktfrequenz sei 48 MHz. 48000000/65536 = 732. Das heißt, die minimal zulässige Frequenz, die unter Verwendung eines 16-Bit-Teilers erhalten werden kann, beträgt 732 Hz. Zu viel. In der Marlin-Firmware beträgt das Minimum 120 Hz (was ungefähr 8 MHz geteilt durch dieselbe Konstante 65536 entspricht). Wir müssen die Register 24 Bit machen. Dann ist die Mindestfrequenz gleich 48000000 / (2 ^ 24) = 48000000/16777216 = 2,861 Hz.

Gut. Stoppen Sie die langweilige Theorie! Lass uns weiter üben! Starten Sie PSoC Creator und wählen Sie Datei-> Neu-> Projekt:



Als nächstes habe ich das Steckbrett ausgewählt, aus dem die Umgebung grundlegende Informationen über den verwendeten Controller und seine Einstellungen entnimmt:



Ich fühle mich bereits bereit, ein Projekt von Grund auf neu zu erstellen, daher wähle ich Leeres Schema :



Geben Sie der Arbeitsumgebung den Namen PSoC3DTest :



Und hier ist er, ein fertiges Projekt!



Als erstes möchte ich meine eigene Komponente basierend auf UDB erstellen. Daher muss ich, wie bereits im letzten Artikel erwähnt, zur Registerkarte Komponenten wechseln:



Klicken Sie mit der rechten Maustaste auf das Projekt und wählen Sie Komponentenelement hinzufügen :



Wir sagen, dass wir ein UDB-Dokument hinzufügen, den Namen in StepperController ändern und auf Neu erstellen klicken müssen:



Die Komponente wurde im Baum angezeigt. Außerdem wurde der Editor dieser Komponente geöffnet:



Platzieren Sie den Datenpfadblock auf dem Formular:



Nachdem wir diesen Block ausgewählt haben, gehen wir zu seinen Eigenschaften und ändern die Bittiefe von 8 auf 24. Die verbleibenden Parameter können unverändert bleiben.



Um alle Blöcke (für alle Motoren) gleichzeitig zu starten, starte ich das Startsignal von außen (füge den Start- Eingang hinzu). Ausgaben: Ich werde Step direkt beenden , damit ich es dem Schrittmotortreiber sowie Out_Idle übermitteln kann . Anhand dieses Signals kann der Prozessor feststellen, dass das Gerät zu dem Zeitpunkt seine Arbeit beendet hat. Die Namen der Schaltkreise, die diesen Ein- und Ausgängen entsprechen, sind in der Abbildung sichtbar.



Bevor ich über die Logik des Automaten spreche, werde ich ein weiteres rein technisches Problem beschreiben: Einstellen der Impulsdauer Schritt . Die DRV8825-Treiberdokumentation verlangt, dass die Impulsbreite mindestens 1,9 μs beträgt. Andere Fahrer stellen weniger Anforderungen an die Breite. Wie bereits im theoretischen Teil erwähnt, sind die vorhandenen Register bereits durch Einstellen der Schrittdauer und der Anzahl der Schritte belegt. Ob es Ihnen gefällt oder nicht, ein 7-Bit-Zähler sollte auf der Schaltung platziert werden. Wir nennen es einen One-Shot, der den Schrittimpuls setzt. Bei einer Frequenz von 48 MHz sollte dieser Zähler mindestens 91,2 Schritte zählen, um eine Dauer von 1,9 μs zu gewährleisten. Runden Sie auf 92 auf. Jeder Wert, der diesen Wert überschreitet, ist nicht geringer. Es stellt sich folgende Einstellung heraus:



Zählername SingleVibrator . Es wird nie zurückgesetzt, daher ist der Reset- Eingang immer mit Null verbunden. Wenn sich die Maschine (unten beschrieben) im Ein-Zustand befindet, wird sie in allen anderen Zuständen geladen (zuerst habe ich die spezifischen Zustände der Maschine ausgewählt, aber es hat sich herausgestellt, dass dies mit einer so kniffligen Methode möglich ist Es werden viel weniger PLD-Ressourcen benötigt, aber das Ergebnis ist das gleiche. Der Ladewert ist dezimal 92. Richtig, ein guter Editor ersetzt diesen Wert sofort durch hexadezimal:



Wenn der Zähler auf Null gezählt wird, meldet er dies der Kette mit dem Namen One_Finished . Mit der Theke - das war's.

Welche Statusflags verwendet unsere Maschine? Ich habe es so verstanden (ich erinnere Sie daran, auf die Liste der Ausgaben in Datapath zu doppelklicken, um sie festzulegen):





Ich werde die Batterie A0 als Zähler für die Impulsdauer verwenden. Wenn ihr Wert Null erreicht, wird ein Flag gespannt, dem ich den Namen Pulse_Finished gegeben habe . Batterie A1 zählt Impulse für mich. Daher wird durch das Nullsetzen das Flag Process_Finished gespannt .

Wir konstruieren den Übergangsgraphen des Automaten:



Die Variable, die ihren Status festlegt, heißt State . Ordnen Sie diese Variable sofort dem Adressregister des ALU-Befehls zu. Zuerst habe ich vergessen, dies zu tun, so dass ich lange Zeit nicht verstehen konnte, warum meine Maschine nicht funktioniert. Doppelklicken Sie auf den Eintragsblock in Datapath:



Und passen:



Wir beginnen mit dem Übergangsgraphen und den damit verbundenen ALU-Anweisungen.

Beginnen wir mit dem Ruhezustand . Es ist ziemlich gesättigt in seinen Handlungen.

Erstens wird der Wert der Datenregister D0 und D1 ständig in die Batterien A0 bzw. A1 gelegt:



Von diesem Eintrag aus sieht das geschulte Auge alles, was Sie brauchen. Da unsere Augen immer noch nicht gesetzt sind, doppelklicken wir auf den Eintrag und sehen dasselbe, aber im Detail:



Der Hauptwert hier ist das Befüllen der Batterie A1, des Impulszählers. Wenn das Programm den Wert D1 eingibt, geht es sofort zu A1. Das Programm wird definitiv keine Zeit haben, den Prozess bis zur nächsten Maßnahme zu starten. Dieser Wert wird überprüft, um eine Bedingung für das Verlassen dieses Zustands zu bilden, dh es gibt keinen anderen Ort, an dem er gefüllt werden kann.

Nun wollen wir sehen, was auf der Ebene der Übergangsgraphen gemacht wird:



Mit dem Hilfsauslöser Start_Prev können Sie am Eingang Start eine positive Flanke abfangen und eine Verzögerungsleitung für 1 Zyklus organisieren. Es enthält immer den Status der Starteingabe , der sich auf der vorherigen Kennzahl befand. Jemand ist besser damit vertraut, dies in Verilog zu sehen:



Gleicher Text
always @ (posedge clock) begin : Idle_state_logic case(State) Idle : begin Start_Prev <= (Start); IsIdle <= (1); if (( Start&(!Start_Prev)&(!Process_Finished) ) == 1'b1) begin State <= One ; end end 


Dementsprechend ist die Bedingung Start & (! Start_Prev) nur dann erfüllt , wenn zwischen den Maßnahmen eine positive Differenz der Startlinie auftritt .

Wenn sich die Maschine in diesem Zustand befindet, wird der IsIdle- Ausgang in einen einzelnen Zustand versetzt, wodurch die externe Umgebung darüber informiert wird, dass der Block passiv ist. Bei diesem Ansatz werden weniger PLD-Ressourcen ausgegeben, als wenn das Konstrukt State == Idle an die Ausgabe übergeben wurde.

Wenn die Differenz des Startsignals von der externen Umgebung stammt und der Akkumulator A1 einen Wert ungleich Null hat, verlässt die Maschine den Ruhezustand . Wenn in A1 Null eingegeben wird, ist die Engine nicht an der Entwicklung dieses Segments beteiligt, so dass die Differenz auf der Startlinie ignoriert wird. Dies gilt für einen nicht verwendeten Extruder. Bei einigen Druckern wird die Z-Achsen-Engine ebenfalls selten verwendet. Ich möchte Sie daran erinnern, wie eine Bedingung gebildet wird, die in A1 einen Nullwert anzeigt (und dessen Umkehrung ungleich Null ist):



Als nächstes tritt die Maschine in den Zustand Eins ein :



In diesem Zustand wird der Schrittausgang auf 1 gesetzt. Ein Schrittimpuls wird an den Treiber angelegt. Außerdem wird der Wert des IsIdle- Triggers zurückgesetzt . Die externe Umgebung wird informiert, dass sich das Gerät in der aktiven Phase befindet.

Dieser Zustand wird durch das One_Finished- Signal verlassen, das auf eins angehoben wird, wenn der Sieben-Bit-Zähler auf Null zählt. Ich möchte Sie daran erinnern, dass das One_Finished- Signal von diesem bestimmten Zähler generiert wird:



Während sich die Maschine in diesem Zustand befindet, lädt die ALU den Wert aus dem Register D0 in die Batterie A0 (Einstellen der Impulsdauer). Lassen Sie mich Ihnen nur eine kurze Notiz zeigen, die Folgendes sagt:



Der geladene Wert wird im folgenden Zustand verwendet. Die Maschine erzeugt eine Verzögerung, die die Impulsdauer festlegt:



Der Step- Ausgang wird auf Null zurückgesetzt. Die Batterie A0 nimmt ab, wie der folgende kurze Eintrag zeigt:



Und wenn Sie darauf doppelklicken - ein vollständiger Eintrag:



Wenn der Wert von A0 Null erreicht, wird das Pules_Finished-Flag gesetzt und die Maschine wird in den Dekrementierungszustand versetzt :



In diesem Zustand nimmt in ALU der Wert des Akkumulators A1 ab, wodurch die Anzahl der Impulse eingestellt wird:



Vollversion des Datensatzes:



Je nach Ergebnis erfolgt ein Übergang entweder zum nächsten Impuls oder zum Ruhezustand . Doppelklicken Sie auf den Status, um die Übergänge unter Berücksichtigung der Prioritäten anzuzeigen:



Eigentlich mit UDB alles. Jetzt machen wir das entsprechende Symbol. Klicken Sie dazu mit der rechten Maustaste auf den Editor und wählen Sie Symbol generieren :



Wir gehen zum Projektdiagramm:



Und wir führen eine Schaltung ein, in der es eine bestimmte Anzahl dieser Steuerungen gibt. Ich habe fünf gewählt (drei Achsen plus zwei Extruder). Drucker mit einer großen Anzahl von Extrudern werden nicht als billig angesehen. Sie können FPGA auf sie setzen. Um die wahre Komplexität zu erkennen, habe ich einen USB-UART-Block (zum Empfangen von Daten von einem Computer oder demselben Raspberry Pi) und einen echten UART (für die Kommunikation mit einem billigen Wi-Fi-Modul ESP8266 oder beispielsweise einem intelligenten Display, das dies kann) verwendet GCODE über UART senden). Ich habe keine PWMs usw. hinzugefügt, da deren Komplexität ungefähr klar ist und das reale System noch weit entfernt ist. Es stellte sich irgendwie so heraus:



Das Steuerregister erzeugt ein Triggersignal, das gleichzeitig an alle Blöcke geht. Lassen Sie außerdem Signale herauskommen, die während der Bildung des Segments statisch sind. Ich habe alle Idle- Ausgänge mit "And" gesammelt und auf den Interrupt-Eingang angewendet. Ich habe eine Unterbrechung an einer positiven Front ernannt. Wenn mindestens ein Motor startet, wird der Interrupt-Eingang zurückgesetzt. Am Ende des letzten Motors wird dieser gespannt, wodurch der Prozessor über die Bereitschaft zur Ausgabe des nächsten Segments informiert wird. Passen Sie nun die Frequenzen an, indem Sie auf das Baumelement Clocks doppelklicken:



Doppelklicken Sie in der angezeigten Tabelle auf das Element PLL_OUT :



Wir werden die Tabelle irgendwie ausfüllen (ich habe die Regeln zum Einrichten dieser Tabelle nicht gut genug verstanden, weshalb ich den Begriff "So etwas" verwende):



Doppelklicken Sie nun auf die Zeile Clock_1 :



Stellen Sie die Taktfrequenz der UDB-Blöcke auf 48 MHz ein:



Da das Projekt experimentell ist, macht es keinen Sinn, eine API dafür zu erstellen. Um das im vorherigen Artikel untersuchte Material zu konsolidieren, wechseln wir erneut zur Registerkarte Komponenten. Klicken Sie für das StepperController-Projekt mit der rechten Maustaste durch das Element Komponente hinzufügen, und fügen Sie zuerst die Header-Datei und dann die C-Quellcodedatei hinzu:





Ich werde oberflächlich die beiden Funktionen der Initialisierung und des Starts des von mir hinzugefügten Segments zeigen. Der Rest ist im Beispiel zum Artikel zu sehen.

 void `$INSTANCE_NAME`_Start() { `$INSTANCE_NAME`_SingleVibrator_Start(); //"One" Generator start } void `$INSTANCE_NAME`_PrepareStep(int nSteps,int duration) { CY_SET_XTND_REG24(`$INSTANCE_NAME`_Datapath_1_D0_PTR, duration>92?duration-92:0); CY_SET_XTND_REG24(`$INSTANCE_NAME`_Datapath_1_D1_PTR, nSteps>1?nSteps-1:0); } 

Ich habe den Namen von main.c durch main.cpp ersetzt , um zu überprüfen, ob die Entwicklungsumgebung normal auf C ++ reagiert, da die Marlin-Firmware objektorientiert ist. Vorhersehbar überschüttete Fehler, die vorhersehbar durch Hinzufügen einer regulären Sache beseitigt wurden:



Gleicher Text
 extern "C" { #include "project.h" } 


Für den weltweiten Start von Motoren habe ich eine solche Funktion gemacht (es ist sehr rau, aber für Experimente mit einem kugelförmigen Pferd im Vakuum reicht es aus, bei Experimenten ist die Entwicklungszeit wichtiger als die Schönheit):
 void StartSteppers() { Stepper_Control_Reg_Write (1); Stepper_Control_Reg_Write (1); Stepper_Control_Reg_Write (1); Stepper_Control_Reg_Write (0); } 

Sie startet das Startsignal für alle Fälle sofort für drei Takte und lässt es dann wieder fallen.

Beginnen wir mit den Experimenten. Gehen Sie zunächst einfach über die X- und Y-Engines (im Beispiel initialisiert die erste Gruppe von Aufrufen alle Controller, die zweite setzt die X- und Y-Controller auf die erforderliche Anzahl von Schritten und startet den Prozess):

 int main(void) { CyGlobalIntEnable; /* Enable global interrupts. */ StepperController_X_Start(); StepperController_Y_Start(); StepperController_Z_Start(); StepperController_E0_Start(); StepperController_E1_Start(); StepperController_X_PrepareStep (10,1000); //    StepperController_Y_PrepareStep (50,500); StartSteppers(); //   for(;;) { } } 

Wir schauen uns das Ergebnis an:



Überprüfen Sie die Dauer des positiven Impulses:



Alles ist richtig. Schließlich überprüfen wir, wie gut der Interrupt funktioniert. Fügen Sie eine globale Zählervariable hinzu:

 static int nStep=0; 

Diese Variable wird in der Hauptfunktion einer zugewiesen und in der Interrupt-Handler-Funktion erhöht. Der Interrupt-Handler wird nur einmal ausgelöst, nur zur Überprüfung. Ich habe es so gemacht:

 extern "C" { CY_ISR(StepperFinished) { if (nStep == 1) { StepperController_X_PrepareStep (5,500); StartSteppers(); nStep += 1; } } } 

Und in der Hauptfunktion habe ich buchstäblich zwei Zeilen hinzugefügt: die Einbeziehung von Interrupts und die Zuweisung dieser Variablen. Und ich weise schon zu, als die Maschinen starteten. Andernfalls kam eine falsche Interrupt-Anfrage. Es gibt keinen besonderen Grund, jetzt dagegen anzukämpfen. Das Projekt ist experimentell.



Gleicher Text
 int main(void) { CyGlobalIntEnable; /* Enable global interrupts. */ isr_1_StartEx(StepperFinished); StepperController_X_Start(); StepperController_Y_Start(); StepperController_Z_Start(); StepperController_E0_Start(); StepperController_E1_Start(); /* Place your initialization/startup code here (eg MyInst_Start()) */ StepperController_X_PrepareStep (10,1000); StepperController_Y_PrepareStep (20,500); StartSteppers(); nStep = 1; for(;;) { } } 


Wir überprüfen das Ergebnis (im zweiten Schritt sollte nur Motor X funktionieren und die Schritte sollten halb so viel werden):



Alles ist richtig.

Fazit


Im Allgemeinen ist bereits klar, dass UDB-Blöcke nicht nur zum Einstellen schneller Hardwarefunktionen verwendet werden können, sondern auch zum Verschieben der Logik von der Software auf die Firmware-Ebene. Leider stellte sich heraus, dass das Volumen des Artikels so groß war, dass es unmöglich erscheint, die Überprüfung abzuschließen und eine eindeutige Antwort zu erhalten, ob die UDB-Funktionen für die endgültige Lösung der Aufgabe ausreichen. Bisher ist nur ein kugelförmiges Pferd in einem Vakuum bereit, dessen Aktionen im Prinzip den erforderlichen sehr ähnlich sind, aber ein nerviger Leser, der mit der Theorie der Schrittmotorsteuerung vertraut ist, wird viele Mängel darin finden. Die vorgestellte Einheit unterstützt keine Beschleunigung, ohne die der Betrieb eines echten Schrittmotors nicht möglich ist. Vielmehr unterstützt es, aber in diesem Stadium wird eine hohe Interruptrate erforderlich sein, und alles wurde konzipiert, um dies zu vermeiden.

Die Genauigkeit der Einstellung der Frequenz des dargestellten Blocks ist bei weitem nicht akzeptabel. Insbesondere wird eine Pulsfrequenz von 40.000 Hz mit einem Teiler von 1200 und 39966 Hz mit einem Teiler von 1201 bereitgestellt. Zwischenfrequenzen zwischen diesen beiden Werten an diesem Gerät sind nicht erreichbar.

Vielleicht gibt es noch einige andere Mängel. Wir werden uns jedoch im nächsten Artikel mit ihnen befassen, um zu überprüfen, ob genügend UDB-Ressourcen vorhanden sind.

In der Zwischenzeit haben die Leser unter anderem ein echtes Beispiel für die Erstellung eines Blocks auf Basis von UDB von Grund auf erhalten. Das Testprojekt, das beim Schreiben dieses Artikels erhalten wurde, kann hier aufgenommen werden .

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


All Articles