Forum: Mikrocontroller und Digitale Elektronik In C mehrere Register per Variable gleichzeitig verändern


von Clerner (Gast)


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

von Walter T. (nicolas)


Lesenswert?

Clerner schrieb:
> immer zusammen ändern
> ohne jedem den Wert einzeln zuweisen zu müssen.

Warum?

von Cyblord -. (cyblord)


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?

von Teo D. (teoderix)


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

von chris (Gast)


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

von Theor (Gast)


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.

von Clerner (Gast)


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.

von Peter D. (peda)


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.

von Theor (Gast)


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.

von georg (Gast)


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.
von Holger (Gast)


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)

von Theor (Gast)


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.

von A. S. (Gast)


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
}

von Clerner (Gast)


Lesenswert?

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.

von Joe F. (easylife)


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.

von Peter D. (peda)


Lesenswert?

Clerner schrieb:
> So komme ich da weiter, es funktioniert.

Ändert aber nichts daran, daß Kettenzuweisungen bad practice sind.
Ein erfahrener Programmierer benutzt sowas nicht.

von chris (Gast)


Lesenswert?

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 ;)

von Clerner (Gast)


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.

von A. S. (Gast)


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.

von Joe F. (easylife)


Lesenswert?

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.

von Clerner (Gast)


Lesenswert?

Achim S. schrieb:
> nicht 3 Mal die gleichen (und irgendwann vielleicht
> komplexe) Rechenvorschriften kopieren zu müssen.

Ja, das war der Hintergedanke.

von Joe F. (easylife)


Lesenswert?

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.

von Dr. Sommer (Gast)


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
}

von Clerner (Gast)


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.

von Theor (Gast)


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?

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

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

von A. S. (Gast)


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.

von Clerner (Gast)


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

von Theor (Gast)


Lesenswert?

Aha. OK. Es ging also, mit anderen Worten, um einen möglichst kurzen 
C-Text. Danke.

von Dr. Sommer (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.