In C mehrere Register per Variable gleichzeitig verändern

Gast #5292173
Lesenswert?

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
#5292184
Lesenswert?

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?
#5292185
Lesenswert?

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...
Gast #5292190
Lesenswert?

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...
Gast #5292220
Lesenswert?

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.
Gast #5292221
Lesenswert?

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.
#5292227
Lesenswert?

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.
Gast #5292239
Lesenswert?

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.
Gast #5292260
Lesenswert?

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.
Gast #5292270
Lesenswert?

> 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)
Gast #5292276
Lesenswert?

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.
Gast #5292279
Lesenswert?

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
}
#5292419
Lesenswert?

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.
Gast #5292473
Lesenswert?

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.
Gast #5292474
Lesenswert?

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.
Gast #5292494
Lesenswert?

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
}
Gast #5292496
Lesenswert?

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.
Gast #5292505
Lesenswert?

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?
Gast #5292642
Lesenswert?

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.
Gast #5292660
Lesenswert?

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ß.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren