Hallo Programmiere seit einiger Zeit in C. Nun habe ich ein Problem das die C Profis sicher leicht lösen könnten. Ich will den Inhalt von mehreren Registerwerten immer zusammen ändern ohne jedem den Wert einzeln zuweisen zu müssen. So sollen 3 Pegisterwerte für die PWM Ausgabe gesetzt werden. Da geht z.B. über PDM1=PDM2=PDM3=128;. Nun will ich den Wert Incrementieren und müsste dann jeweils einen Wert nehmen und dann den anderen zuweisen. Ist so etwas umständlich da ja unmittelbar nach jeder Operation eine Wertezuweisung eingebaut werden müsste. Ich konnte keine Funktion finden die es erlaubt alle 3 Werte mit einer Variable zu ändern, also das ich z.B. PWM++ scheiben könnte und dann alle 3 geänderten Werte ins jeweilige Register geschrieben würde. Müsste doch irgendwie gehen, konnte dazu bisher nichts Finden. Ich hoffe es ist klar was ich meine. Danke
Clerner schrieb: > Ich konnte keine Funktion finden die es erlaubt alle 3 Werte mit einer > Variable zu ändern, also das ich z.B. PWM++ scheiben könnte und dann > alle 3 geänderten Werte ins jeweilige Register geschrieben würde. Das geht auch einfach nicht. Es gibt auf CPU Ebene keinen Befehl der das kann. Auf C Ebene kannst du dir natürlich eine Funktion schreiben welche einen Wert bekommt und dann 3 Variabeln ändert, aber natürlich wird das trotzdem nacheinander passieren. > Müsste doch irgendwie gehen Warum denkst du das?
Clerner schrieb: > Ich konnte keine Funktion finden die es erlaubt alle 3 Werte mit einer > Variable zu ändern, also das ich z.B. PWM++ scheiben könnte und dann > alle 3 geänderten Werte ins jeweilige Register geschrieben würde. Wenn du Ahnung von µCs hättest, wüstest du das das nicht möglich ist. Beschäftige dich doch mal wenigstens rudimentär mit Assembler. Rein C technisch packt man das in eine Funktion. Weniger tippen, mehr Struktur, bessere Lesbarkeit...
Clerner schrieb: > Ich konnte keine Funktion finden die es erlaubt alle 3 Werte mit einer > Variable zu ändern geht auch nicht auch trotz Hochsprache!! Die CPU kann immer nur ein/e Register/Speicherzelle im RAM bearbeiten oder kannst du Bus und Auto gleichzeitig fahren ?? Das was du meinst ist der ANSCHEIN der Verarbeitungsgeschwindigkeit der Befehle das es so aussieht als ob die CPU es "gleichzeitig" bearbeiten würde. Es wird alles Stumpf hintereinander verarbeitet. inc pwm1 inc pwm2 inc pwm3 Auch das setzen geht nur nacheinander lade 128 ins PWM1 lade 128 ins PWM2 lade 128 ins PWM3 So erkennst du das diese Routine, die es nicht gibt, auch nicht umsetztbar wäre... Als Tipp CPU läuft mit 4 Mhz = 250ns wenn ein Befehl einen Takt bräuchte. Wie lange jetzt eine das in C braucht weiß ich leder nicht da hängt meistens ja noch ein wenig mehr dran...
C selbst bietet solch ein Sprachmittel nicht. Je nach uC, wäre das simultane setzen von Registerwerten evtl. dennoch möglich, wenn man gewisse Eigenschaften der Datentypen und der unterliegenden Maschineninstruktionen ausnutzt. Wie genau und mit welchen Nachteilen, müsste man sich dann überlegen, wenn man den uC kennt. Insbesondere die Wortbreite und die verfügbaren arithmetischen Befehle sind da interessant.
chris schrieb: > Auch das setzen geht nur nacheinander > > lade 128 ins PWM1 > lade 128 ins PWM2 > lade 128 ins PWM3 Das die Zuweisungen nicht in einem Takt erfolgen ist schon klar, ging mir auch nicht um die Ausführungsgeschwindigkeit. Deshalb ist diese Unterstellung fehl am Platze: >Wenn du Ahnung von µCs hättest, wüstest du das das nicht möglich ist. PDM1=PDM3=PDM3=128; akzeptiert der Compiler und es Funktioniert auch, Ziel ist es halt nicht jedesmal alle drei Zuweisungen hinschreiben zu müssen. Die Zuweisung in eine Funktion zu schreiben geht natürlich nur müsste ich dann wenn ich die 'berechnete Variable' habe immer danach die Funktion aufrufen was vom Schreiben her aufs selbe rauskommt. dachte nur das es eine Möglichkeit gibt das zu vereinfachen.
Clerner schrieb: > Da geht z.B. über > PDM1=PDM2=PDM3=128; Das sieht zwar lustig aus, kann aber Seiteneffekte haben und dauert am längsten, da 2 unnötige Lesezugriffe erfolgen. Diese Schreibweise compiliert nämlich zu:
1 | PDM3=128; |
2 | PDM2=PDM3; |
3 | PDM1=PDM2; |
Es gibt nur wenige Architekturen, die ein direktes MOV von IO-Registern implementiert haben (z.B. der 8051 kann ein "MOV SFR, SFR"). Sauber macht man es daher so:
1 | PDM1=128; |
2 | PDM2=128; |
3 | PDM3=128; |
Exakt gleichzeitig geht natürlich nicht, da eine CPU immer sequentiell arbeitet.
Clerner schrieb: > [...] > PDM1=PDM3=PDM3=128; akzeptiert der Compiler und es Funktioniert auch, > Ziel ist es halt nicht jedesmal alle drei Zuweisungen hinschreiben zu > müssen. > [...] Vielleicht verstehe ich Deine Absicht nicht. Aber es scheint mir, dass Du eine Registerwert zuweisen willst, ohne dem Compiler zu sagen, um welches Register es sich handelt. Das geht (wenn man keine Tricks verwendet) definitiv nicht. Die Form, die Du da geschrieben hast, ist meines Wissens, die kürzeste mögliche Form in reinem C - ohne Berücksichtigung der konkreten Registeradressen und Datentypen.
Clerner schrieb: > Ziel ist es halt nicht jedesmal alle drei Zuweisungen hinschreiben zu > müssen. Das ist theoretisch ganz einfach: du brauchst einen Compiler, der Gedanken lesen kann. Aber wenn du wirklich Befehle ausgeführt haben willst, die du nicht gegeben hast, wirst du wahrscheinlich unliebsame Überraschungen erleben. Georg
Beitrag #5292263 wurde von einem Moderator gelöscht.
Beitrag #5292266 wurde von einem Moderator gelöscht.
Beitrag #5292268 wurde von einem Moderator gelöscht.
Beitrag #5292269 wurde von einem Moderator gelöscht.
> Da geht z.B. über > PDM1=PDM2=PDM3=128;. Nun will ich den Wert Incrementieren und müsste > dann jeweils einen Wert nehmen und dann den anderen zuweisen. und wenn du zum Incrementieren einfach PDM1=PDM2=++PDM3; schreibst? ... das könnte man sogar noch in 'nem Macro verstecken #define INC_PDM() (PDM1=PDM2=++PDM3)
Ich habe etwa folgende Idee: 1. Verlagerung des Inkrements zeitlich vor das setzen der Register. unter der Voraussetzung, daß: a) Die CPU eine größere Wortbreite hat, als die Register breit sind b) Die Endianess passt oder mit wenig Aufwand passend gemacht werden kann, falls nicht ohnehin lediglich Literale verwendet werden können c) die fraglichen Register memory mapped innerhalb der Addressweite eines vorhanden Datentyps liegen d) der Speicher- resp. Datenbus die notwendige Wortbreite hat und möglicherweise d) die CPU Arithmetik (oder möglicherweise auch Logik-Befehle) hat, die auf dieser großen Wortbreite operieren mit dem evtl. (und wahrscheinlichen) Nachteil, dass der gesamte Vorgang von Inkrementieren und setzen der Register länger dauert als das einzelne Setzen mit Peters zweiter Variante ohne Rücklesen der Register, aber mit dem Vorteil, dass das setzen der Register simultan erfolgt. also etwa:
1 | // seien
|
2 | volatile uint8_t PDM1, PDM2, PDM3; // mit zur Endianess passenden auf einander folgenden Adressen |
3 | |
4 | uint32_t PWM_Wert = 128 + 128 << 8 + 128 << 16; |
5 | |
6 | PWM_Wert += 1 + 1 << 8 + 1 << 16; |
7 | |
8 | PDM1 = PWM_Wert; |
Das ist zugegeben dirty, sollte aber funktionieren, denke ich. (Man müsste natürlich noch berücksichtigen, dass angrenzende Register nicht verändert werden sollen). Aber: Hier verlasse ich eindeutig, dass was der C-Standard aussagt und zusagt. Das ist ein Trick - keine einfach nur kürzere Ausdrucksweise.
Falls es nur um die hypothetische Möglicheit geht, hier 3 davon:
1 | /* Varianten*/
|
2 | #define REG_UPDATE(x) {PDM1=PDM2=PDM3=(x);} (void) 0
|
3 | |
4 | void RegUpdate(int x) |
5 | {
|
6 | PDM1=PDM2=PDM3=x; |
7 | }
|
8 | |
9 | int main(...) |
10 | {
|
11 | REG_UPDATE(11); |
12 | RegUpdate( 11); |
13 | REG_UPDATE(++PDM3); /* Beachte ++PDM3 statt PDM3++! */ |
14 | /* oder direkt */
|
15 | PDM1=PDM2=++PDM3; |
16 | /* oder mit ein bisschen Mathe */
|
17 | RegUpdate( 2*PDM3+17); |
18 | REG_UPDATE(2*PDM3+17); |
19 | }
|
Danke Achim So komme ich da weiter, es funktioniert. Hätte ja auch selbst darauf kommen können das bei der Parameterübergabe auch gerechnet werden kann.
Mal so gefragt: Wenn eh alle 3 Variablen oder Register immer den gleichen Wert beinhalten sollen, warum nimmst du nicht einfach nur 1 Variable/Register für deine 3 PWMs? Bzw. noch weiter gedacht: vermutlich sind ja auch dann die PWMs identisch, und man bräuchte auch nur 1 PWM.
Clerner schrieb: > So komme ich da weiter, es funktioniert. Ändert aber nichts daran, daß Kettenzuweisungen bad practice sind. Ein erfahrener Programmierer benutzt sowas nicht.
Clerner schrieb: > Deshalb ist diese Unterstellung fehl am Platze: >>Wenn du Ahnung von µCs hättest, wüstest du das das nicht möglich ist. kam nicht von mir ;)
Joe F. schrieb: > Wenn eh alle 3 Variablen oder Register immer den gleichen Wert > beinhalten sollen, warum nimmst du nicht einfach nur 1 Variable/Register > für deine 3 PWMs? > > Bzw. noch weiter gedacht: vermutlich sind ja auch dann die PWMs > identisch, und man bräuchte auch nur 1 PWM. Die Werte sind ja Registerwerte (Variable != Register) für jeweilige PWM DutyCycles die jeweils für eine Phase zuständig sind, der Gedanke war ja eben mittels einer Variable diese einzustellen. Ist also für eine blockkommutierte BLDC Ansteuerung. Somit sind nur die DutyCycles identisch aber nicht die PWMs.
Peter D. schrieb: > Kettenzuweisungen bad practice sind. Ja, zumindest wenn die Möglichkeit besteht, dass sie unterschiedliche Breite haben, Volatile sind, ... Aber sobald er Funktionen oder Defines erlaubt, geht es ja auch ohne:
1 | define REG_UPDATE(x) {int v=(x); PDM1=v; PDM2=v; PDM3=v;} (void) 0 |
2 | |
3 | void RegUpdate(int x) |
4 | {
|
5 | PDM1=x; |
6 | PDM2=x; |
7 | PDM3=x; |
8 | }
|
Ich vermute, wir haben das eigentliche Anliegen des TO nicht erkannt (ich zumindest überhaupt nicht!). Es ging nicht um die Kettenzuweisung, sondern darum, nicht 3 Mal die gleichen (und irgendwann vielleicht komplexe) Rechenvorschriften kopieren zu müssen. Und da hat er nunmal einfach Recht.
Clerner schrieb: > Somit sind nur die DutyCycles > identisch aber nicht die PWMs. Und somit kannst du auch für alle 3 PWMs eine gemeinsame Variable/Register für den Duty-Cycle benutzen.
Achim S. schrieb: > nicht 3 Mal die gleichen (und irgendwann vielleicht > komplexe) Rechenvorschriften kopieren zu müssen. Ja, das war der Hintergedanke.
Achso, jetzt verstehe ich erst. PDM1, PDM2, PDM3 sind Hardware-Register. Da bleibt dir dann natürlich nur die Möglichkeit sie so schnell wie möglich nacheinander zu beschreiben. Anders gehts ja nicht.
Weil's so schön ist nochmal in C++ (mit -std=c++17 kompilieren):
1 | #include <cstdint> |
2 | |
3 | template <auto&... Refs> |
4 | struct MultiRegSetter { |
5 | template <typename Src> |
6 | Src operator = (const Src& x) { |
7 | ((Refs = x), ... ); |
8 | }
|
9 | };
|
10 | |
11 | volatile uint32_t PDM1, PDM2, PDM3; |
12 | MultiRegSetter<PDM1, PDM2, PDM3> SetPDMMulti; |
13 | |
14 | int main () { |
15 | SetPDMMulti = 42; |
16 | }
|
Joe F. schrieb: > Und somit kannst du auch für alle 3 PWMs eine gemeinsame > Variable/Register für den Duty-Cycle benutzen. Natürlich nicht, denn die PWM Kanäle haben jeder ein eigenes Register (und somit eine eigenen festgelegten Namen) für den Duty Cycle das einzeln beschrieben werden muß. Es gibt zwar Prozessoren die auch ein gemeinsames DutyCycle Register für alle PWM Kanäle aktivierbar haben, nur macht das keinen Sinn wenn man z.B. 2 Motoren steuern will.
Hm. Ich habe irgendwie auch den Eindruck, dass mir die Absicht von Clerner nicht klar ist. Worum geht es? 1. Einen möglichst kurzen C-Text? 2. Eine möglichst kurze Ausführungszeit? 3. Ein simultanes Ändern der Register? 4. Eine Kombination davon? 5. Irgendwas ganz Anderes? Schreibt man das mal ganz geradeaus hin, dann sollte das so aussehen:
1 | PWM_Wert = <irgendeine Gleichung>; |
2 | |
3 | PDM1 = PWM_Wert; |
4 | PDM2 = PWM_Wert; |
5 | PDM3 = PWM_Wert; |
Ist das eine Lösung für Dich, Clerner? Oder was ist für Dich daran unerwünscht oder problematisch?
hallo all, das gelaber hier erscheint mir nutzlos/sinnfrei, wenn der to es nicht schaft den uc (pic/avr/arm/...?) zu benennen! weil, der prozessor kontext entscheidet dann über sinn/unsinn dieser frage. mt
Theor schrieb: > Worum geht es? Der TO ist C-Anfänger, möchte Code-kopien vermeiden und hat unglücklicherweise geschrieben, dass er auf einem uC Register beschreiben will. Es geht weder um den konkreten uC-Typ noch um Register noch um Performance, sondern einfach um einen Tipp, den Code zu verbessern.
Für das Problem, wie so was in C gelöst werden kann spielt der Prozesseor eigentlich kaum eine Rolle, das Problem kann bei vielen Controllern auftreten. Ich bin momentan mit einem dsPIC33 zu Gange. Die Lösung von Dr.Sommer trifft es eigentlich recht gut. Leider habe ich kein C++. Es ging ja darum die 3Register in einem Rutsch zu beschreiben damit bei komplexeren Rechnungen, so die nötig werden, nicht immer der ganze Rattenschwanz für die Zuweisung mit rumkopiert werden muß. Also das Ganze auch übersichtlicher zu machen indem nur eine Variable manipuliert werden muß.
Aha. OK. Es ging also, mit anderen Worten, um einen möglichst kurzen C-Text. Danke.
Clerner schrieb: > Leider habe ich > kein C++. Das kostet doch nix, es gibt freie C++-Compiler wie GCC...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.