Forum: Mikrocontroller und Digitale Elektronik Laufzeit Optimierung AtMega2560 als Frequenzgenerator


von Roger S. (roger1004)


Angehängte Dateien:

Lesenswert?

Hey zusammen,
ich brauche mal eure Hilfe. Ich versuche im Moment einen 
Frequenzgenerator mit einem AtMega2560 zu bauen.
Anforderungen sind Dreieck und Rechteckspannung bei 10 bis 250Hz und 
einstellbarer Amplituden Höhe mit Prozent.

Ich habe bis jetzt nur den Dreieckpart Programmiert da ich diesen für 
schwieriger halte.
Ich habe mich dazu entschieden einen Signal Durchlauf (eben mit 10 bis 
250Hz) in 512 einzelnen Schritten zu Programmieren um da hinzubekommen 
habe ich das Fast PWM des AtMegas genommen und Setze die Grenzwerte so 
das während eines Signal Durchlaufes 512 Interrupts ausgelöst werden 
Davon werden bei 256mal immer die Positive Halbwelle verlängert und die 
anderen 256 diese halt immer Reduziert wobei dann ja am Ende eine 
Dreieckspannung entsteht nachdem man das ganze durch einen 
Tiefpassfilter geschickt habe.
Soweit funktioniert das ganze auch. Nur bei höheren Frequenzen stimmt 
meine Berechnungsformel anscheinend nicht mehr weswegen ich dann die 
Werte passend ausprobiert habe die in das OCRnA geschrieben werden 
müssen.
1
uint16_t berchnungOCR1A(int32_t frequenz)
2
{
3
  int32_t frequenzEinesBruchteilsDerAusgangsFrequenz = frequenz * 512; //nimmt die gewünschte Frequenz und berechnet die Impulszeit wenn das Signal mit 512 einzelnen Impulsen dargestellt wird
4
  return (((16000000 / frequenzEinesBruchteilsDerAusgangsFrequenz) / 2)); //Berechnet den Endwert der Funktion abhängig davon wie oft der gewählte Timer hochzählt
5
}


Könnte durch auch sein das hier bereits ein Fehler in meinen 
Einstellungen der Timer ist. Allerdings reicht mir eine Ansteuerung in 
10Hz Stufen bereits aus weswegen ich mit den ermittelten Werten auch 
weiter machen könnte.

Das ist aber nicht mein konkretes Problem, den mit den ermittelten 
Werten kann ich für mich ein ausreichendes Signal Erzeugen. Problem ist 
das ich zu der Frequenz Erzeugung noch andere Aufgaben bearbeiten muss 
und zwar:
Soll ein 1s Timer laufen damit das Signal nach einer bestimmten Zeit 
auch beendet wird
Und es sollen noch Tasten genutzt werden können um das Signal auch 
manuel Stoppen kann

Das beides funktioniert bei dem 10Hz Signal zwar noch allerdings kommt 
der Controller bei höheren Frequenzen wohl nicht mehr dazu die anderen 
Funktionen zu bearbeiten weswegen der Timer steht und und die Tasten 
Auswertung übergangen wird.

Unten sieht man den Teil des Programmes der sich mit der sich explizit 
mit der Signal Erzeugung beschäftigt. Und angehangen ist das ganze 
Projekt. Mit dem kompletten Code geschrieben in der Arduino IDE.
Dabei ist auch die Ansteuerung eines UTFT Displays, die ist aber nur 
notwendig wenn das Dreieck bzw. Rechteck Signal nicht von nöten ist.
Der Teil der irgendwann nicht mehr ausgeführt befindet sich in der Loop.
Vielleicht hat ja jemand eine Idee wie ich die Signal Erzeugung umsetzen 
könnte und währenddessen noch andere Aufgaben erledigen kann.
Ich wäre auch für ganz andere Ideen offen als über einen Interrupt in 
einem Timer das Signal zu Erzeugen falls ich generell falsch an die 
Sache gegangen bin.
Ich hoffe ich konnte rüber bringen wie ich daran gegangen.



Edit: Die kleinere der beiden Dateien reicht völlig zum Download. Erst 
konnte ich keine von beiden anhängen und dann waren beide da :D
1
#include "Arduino.h"
2
// Period = 1 ms => Frequenz = 1kHz
3
uint16_t periodendauer, aktuellerWert = 0;
4
bool richtung = 0; //0=aufwärts 1=abwärts
5
float prozent = 1;
6
7
void initTimer()
8
{
9
  cli();
10
11
  //set WGM13 for PWM, phase and frequency correct
12
  TCCR1A = (1 << COM1A1);
13
  //CS10= prescaler none
14
  TCCR1B = (1 << WGM13);
15
  DDRB |= (1 << PB5);
16
17
  //set WGM13 for PWM, phase and frequency correct
18
  TCCR4A = (1 << COM4C1);
19
  //CS10= prescaler none
20
  TCCR4B = (1 << WGM43);
21
  DDRH |= (1 << PH5);
22
23
  sei();
24
}
25
26
void enableTimer_1o4(uint8_t timer)
27
{
28
  prozent = ((counter_Power + 1) * 5) / 100;
29
30
  uint16_t wertVergleichsRegister[25] = {
31
    635,//10Hz
32
    448,//20Hz
33
    365,//30Hz
34
    316,//40Hz
35
    282,//50Hz
36
    258,//60Hz
37
    239,//70Hz
38
    223,//80Hz
39
    211,//90Hz
40
    200,//100Hz
41
    190,//110Hz
42
    182,//120Hz
43
    175,//130Hz
44
    169,//140Hz
45
    163,//150Hz
46
    158,//160Hz
47
    153,//170Hz
48
    149,//180Hz
49
    145,//190Hz
50
    141,//200Hz
51
    138,//210Hz
52
    135,//220Hz
53
    132,//230Hz
54
    129,//240Hz
55
    126,//250Hz
56
  };
57
58
  periodendauer = wertVergleichsRegister[counter_Hertz];
59
60
  cli();
61
  if (timer == 0)
62
  {
63
    //Timer1_OVF_vect freigeben
64
    TIMSK1 |= (1 << TOIE1);
65
    TCCR1B |= (1 << CS10);//Prescaler auf Enable
66
67
68
    ICR1 = periodendauer;
69
    OCR1A = ((periodendauer / 2) - 1);
70
71
  }
72
  else if (timer == 1)
73
  {
74
    //Timer1_OVF_vect freigeben
75
    TIMSK4 |= (1 << TOIE4);
76
    TCCR4B |= (1 << CS40); //Prescaler auf Enable
77
78
    ICR4 = periodendauer;
79
    OCR4C = ((periodendauer / 2) - 1);
80
  }
81
  TIMSK5 |= (1 << OCIE5A);
82
  sei();
83
}
84
85
void disableTimer_1u4()
86
{
87
  cli();
88
  TCCR1B &= ~(1 << CS10);//Prescaler auf disable
89
  TIMSK1 &= ~(1 << TOIE1);//disable OVF Interrupt
90
  TCCR4B &= ~(1 << CS40); //Prescaler auf disable
91
  TIMSK4 &= ~(1 << TOIE4); //disable OVF Interrupt
92
93
  TIMSK5 &= ~(1 << OCIE5A);
94
  sei();
95
}
96
void signalErzeugung()
97
{
98
  if (richtung)
99
  { //abwärts
100
    if (aktuellerWert > 0)
101
    {
102
      aktuellerWert--;
103
    }
104
    else
105
    {
106
      aktuellerWert++;
107
      richtung = 0;
108
    }
109
  }
110
  else
111
  { //aufwärts
112
    if (aktuellerWert < periodendauer)
113
    {
114
      aktuellerWert++;
115
    }
116
    else
117
    {
118
      aktuellerWert--;
119
      richtung = 1;
120
    }
121
  }
122
}
123
124
ISR (TIMER1_OVF_vect)
125
{
126
  signalErzeugung();
127
  uint16_t test = aktuellerWert * prozent;
128
  OCR1A = test;
129
}
130
131
ISR (TIMER4_OVF_vect)
132
{
133
  signalErzeugung();
134
  uint16_t test = aktuellerWert * prozent;
135
  OCR4C = test;
136
}

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Roger S. schrieb:

> ich brauche mal eure Hilfe. Ich versuche im Moment einen
> Frequenzgenerator mit einem AtMega2560 zu bauen.

Nun, deine Erklärungen, was du tun willst, der Code und die Kommentare 
im Code passen an unzähligen Stellen nicht zusammen.

Bitte erstmal selbst aufräumen, bevor du andere belästigst.

von Elektroniker (Gast)


Lesenswert?

Die Methode ist normal etwas anders.
Du nimmst zuerst ein Sample von Sinus (Dreieck, Rechteck) mit z.B. 512 
Werten und schiebst diesen Sample über einen 8-Bit Parallelausgang auf 
einen D/A-Wandler.
Wenn du variable Verzögerungen einbaust, dann kannst du die Frequenz 
ändern.
Ohne Verzögerungen hast du eine maximale Frequenz.
Noch höher kommst du, wenn du nur jeden 2. Sample ausgibst.

von Elektroniker (Gast)


Lesenswert?

D/A-Wandler gibt es als Widerstandsnetzwerk.

von OMG (Gast)


Lesenswert?

Roger S. schrieb:
> int32_t frequenzEinesBruchteilsDerAusgangsFrequenz = frequenz * 512;

Wennmansolchelangenvariablennamenverwendetmussdasjaschiefgehen.

von Falk B. (falk)


Lesenswert?

Roger S. schrieb:
> Hey zusammen,
> ich brauche mal eure Hilfe. Ich versuche im Moment einen
> Frequenzgenerator mit einem AtMega2560 zu bauen.
> Anforderungen sind Dreieck und Rechteckspannung bei 10 bis 250Hz und
> einstellbarer Amplituden Höhe mit Prozent.

Klingt ja recht machbar.

> Ich habe bis jetzt nur den Dreieckpart Programmiert da ich diesen für
> schwieriger halte.

Nicht wirklich.

> Ich habe mich dazu entschieden einen Signal Durchlauf (eben mit 10 bis
> 250Hz) in 512 einzelnen Schritten zu Programmieren um da hinzubekommen
> habe ich das Fast PWM des AtMegas genommen und Setze die Grenzwerte so
> das während eines Signal Durchlaufes 512 Interrupts ausgelöst werden
> Davon werden bei 256mal immer die Positive Halbwelle verlängert und die
> anderen 256 diese halt immer Reduziert wobei dann ja am Ende eine
> Dreieckspannung entsteht nachdem man das ganze durch einen
> Tiefpassfilter geschickt habe.

Kann man so machen.

> Soweit funktioniert das ganze auch. Nur bei höheren Frequenzen stimmt
> meine Berechnungsformel anscheinend nicht mehr weswegen ich dann die
> Werte passend ausprobiert habe die in das OCRnA geschrieben werden
> müssen.

Ohje ;-) Generation PISA?


> uint16_t berchnungOCR1A(int32_t frequenz)
> {
>   int32_t frequenzEinesBruchteilsDerAusgangsFrequenz = frequenz * 512;

AUA! Bist du Kettenwortliebervereinvorsitzender?

> Das ist aber nicht mein konkretes Problem, den mit den ermittelten
> Werten kann ich für mich ein ausreichendes Signal Erzeugen. Problem ist
> das ich zu der Frequenz Erzeugung noch andere Aufgaben bearbeiten muss
> und zwar:

Das ist der Normalfall, erreicht man mit einem Interrupt und 
Multitasking.

> Soll ein 1s Timer laufen damit das Signal nach einer bestimmten Zeit
> auch beendet wird
> Und es sollen noch Tasten genutzt werden können um das Signal auch
> manuel Stoppen kann

Trivial.

> Das beides funktioniert bei dem 10Hz Signal zwar noch allerdings kommt
> der Controller bei höheren Frequenzen wohl nicht mehr dazu die anderen
> Funktionen zu bearbeiten weswegen der Timer steht und und die Tasten
> Auswertung übergangen wird.

Dann hast du einen Programmier- oder Konzeptfehler.

> Unten sieht man den Teil des Programmes der sich mit der sich explizit
> mit der Signal Erzeugung beschäftigt.

"Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang"

> Vielleicht hat ja jemand eine Idee wie ich die Signal Erzeugung umsetzen
> könnte und währenddessen noch andere Aufgaben erledigen kann.
> Ich wäre auch für ganz andere Ideen offen als über einen Interrupt in

Besorg dir mal ein gescheites Buch zum Thema C und lerne, daß Funktionen 
etc. NICHT in Headerdateien (.h) gehören! Die gehören in Quelldateien 
(.c)

Zu deinem Kernproblem. Ein einfaches Rechtecksignal konstanter Amplitude 
kann man mit einem einfachen Digitalausgang erzeugen. Mittels eines 16 
Bit Zählers, CTC Mode und Output Compare Funktion. Das ist trivial, 
steht alles im Datenblatt. Ein Dreiecksignal kann man so nicht erzeugen. 
Du hast aber schon mal den richtigen Ansatz. Mittels PWM gibt man ein 
Signal aus, daß nach einer Tiefpaßfilterung dann das gewünschte Signal 
ergibt. Das ist eine Form von DA-Wandler. Wenn man 250Hz Dreieck 
erzeugen will, muss man, wenn das Dreieck als solches noch erkennbar 
sein soll, auch mit ausreichend Oberschwingungen rechnen. Außerdem 
sollte die DA-Ausgabefrequenz (Abtastfrequenz) möglichst hoch sein, um 
den Filteraufwand zu minimieren. Pi mal Daumen hat ein 250 Hz Dreieck 
vielleicht bis 5kHz nennenswerte Signalanteile, die man mit min. 10kHz 
ausgeben muss. Nehmen wir 20kHz. Also musst du eine PWM konfigurieren, 
die mit mindestens 20kHz läuft. In dieser ISR wird dann aus einer 
Tabelle ein Datenwert gelesen und in das OCR0A Register geschrieben. Man 
könnte es auch berechnen, ist bei Dreieck ja eher einfach. Die 
verschiedenen Frequenzen erreicht man mittels DDS-Akkumulator. Das 
ist praktisch nichts weiter, als einfache Festkommaaritmetik. Etwa 
so.

Beitrag "Re: SPWM auf Atmega8, bitte um Feedback hinsichtlich Optimierung"

Die variable Amplitude erreicht man dadurch, indem man die Tabellenwerte 
mit einer Variable, der Amplitude mutlipliziert und anschließend 
skaliert. Sprich, die Tabellenwerte sind einfache vorzeichenbehaftete 
Bytes von -127 - 127. Die werden mit einer 8 Bit Variable mit dem 
gleichen Wertebereich multipliziert. Das ergibt eine vorzeichenbehaftete 
16 Bit Zahl. Die ist zu groß für OCR0A. Also wird skaliert, indem man 
die untersten 8 Bit wegwirft und nur die oberen 8 Bits in OCR0A 
schreibt.

Noch ein paar Tips zum Quelltext.

In ISRs möglichst keine Funktionen aufrufen, sondern alles direkt 
hinschreiben. Denn bei Funktionsaufrufen in einer ISR muss der Compiler 
deutlich mehr Register retten und wieder herstellen, das kostet 
wertvolle Takte, besonders bei hochfrequenzen ISRs.

Wozu brauchst du 2 ISRs, die das gleiche machen?

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Sooo, ich war mal so frei, das Chaos ein wenig aufzuräumen.

1.) Alle Dateien sortiert und getrennte .cpp und .h Dateien erstellt
2.) Die Arrays mit den Dateinamen in den Flash geschoben, spart 
wertvollen RAM.

https://www.arduino.cc/reference/en/language/variables/utilities/progmem/

3.) Die vielen Warnungen über die konstanten Strings in den 
Funktionsaufrufen durch Umweg über variablen String vermieden. Man will 
beim Compilieren praktisch immer NULL Warnungen haben, denn dann muss 
man nicht schauen, ob die Warnung wichtig oder unwichtig ist. Mit den 
Warnungen aus den Bibliotheken muss man wohl oder übel leben, wenn man 
nicht die Drecksarbeit machen will, und diese Fehler korrigieren will 
(meistens eher nicht)
1
  // Original
2
  myFiles.load(0, 0, 800, 480, "Start-Screen.raw", 1 , 1);
3
4
  // Neu
5
  strcpy_P(filename, PSTR("Start-Screen.raw"));
6
  myFiles.load(0, 0, 800, 480, filename, 1 , 1);

4.) Die Arrays mit den systematischen Namen kann man in diesem Fall 
einsparen und die Dateinamen berechnen. Etwa so.
1
  itoa(counter_Hertz*10, filename, 10);  // Index in ASCII-String umwandlen
2
  strcat_P(filename, PSTR("Hz.raw""));   // Endung anfügen
3
4
  itoa((counter_Power+1)*5, filename, 10);  
5
  strcat_P(filename, PSTR("%.raw""));
6
7
  itoa(counterSekunden, filename, 10);
8
  strcat_P(filename, PSTR(".raw""));

Strukturierte Programmierung auf Mikrocontrollern

ISBN: 3897215675

5.) Du braucht hier keine Sekunde irgendwelche Floats, erst recht NICHT 
in einem hochfrequenten Interrupt!!! Das macht man mit 
Festkommaarithmetik, im einfachsten Fall mit 16 Bit, bei dem man 
nach der Multiplikation die unteren 8 Bit ignoriert!
1
ISR (TIMER1_OVF_vect)
2
{
3
  // keine Funktionsaufrufe in einer ISR, das kostet wertvolle Takte für die Registersicherung und Widerherstellung
4
  signalErzeugung();
5
  // SO NICHT! Sinnlose Fließkommaoperation
6
  uint16_t test = aktuellerWert * prozent;
7
  OCR1A = test;
8
}
9
10
// eher so
11
12
ISR (TIMER1_OVF_vect)
13
{
14
  static uint16_t i;
15
  i += f_inc;
16
  OCR1A=pgm_read_word(&waveform[i>>8]);
17
}

von weiter weg (Gast)


Lesenswert?

Falk B. schrieb:
> Sooo, ich war mal so frei, das Chaos ein wenig aufzuräumen.

Respekt dass du dich mit dem Arduino-Sch.... so intensiv
auseinandersetzt. Wäre ja alles schön und gut wenn da nicht
immer dieses setup() und loop() da wären ;-)

Naja, man hat ja sonst nix zu tun auf dieser Welt ....

von Roger S. (roger1004)


Lesenswert?

So, Vielen Dank für eure Hilfen besonders dir Falk.

ich habe jetzt viel Zeit damit verbracht das ganze zu Optimieren.
In dem Zuge habe ich auch gemerkt warum meine Funktion mit dem etwas 
langgeraten Variablen Namen nicht richtig funktioniert hat :D

Zu meinem Programaufbau mit den Funktionen in header Dateien.
Dass das nicht die schönste Art und Weise zu Programmieren ist ist mir 
bekannt aber für die Übersichtlichkeit finde ich es hilfreicher als 
nochmal das doppelte an Dateien rumfliegen zu haben. Meines Wissens hat 
das ganze auch keine Programmtechnischen Nachteile, wenn ich aber das 
nächste mal hier ein Programm hochlade passe ich es aber an.

Deine Vorschläge den Dateinamen immer zusammen zu bauen statt die haufen 
an Strings zu speichern werde ich umsetzen sobald die Signal Erzeugung 
funktioniert.


Ich habe versucht meine ISR Routine weiter aufzuräumen allerdings ohne 
merklichen Erfolg bei der Bearbeitungszeit.
1
ISR (TIMER1_OVF_vect)
2
{
3
  //Erzeugen des Signalverlaufs
4
 if (richtung)
5
  { //abwärts
6
    if (aktuellerWert > 0)
7
      aktuellerWert--;
8
    else
9
      richtung = 0;
10
  }
11
  else
12
  { //aufwärts
13
    if (aktuellerWert < 100)
14
      aktuellerWert++;
15
    else
16
      richtung = 1;
17
  }
18
  //Ausgeben des Signals
19
  OCR1A = (((periodendauer*aktuellerWert)/100)* (prozent /100));
20
}

Statt der if else Verschachtelung habe ich auch die Umsetzung mit einem 
Array versucht aus dem der aktuelle Wert geholt wird. Allerdings ohne 
Besserung der Bearbeitungszeit. Das ganze sieht dann so aus:
1
uint16_t test2 = 0;
2
3
const uint8_t test[] = {
4
  0  , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10  , 11  , 12  , 13  , 14  , 15  , 16  , 17  , 18  , 19  , 20  , 21  , 22  , 23  , 24  , 25  , 26  , 27  , 28  , 29  , 30  , 31  , 32  , 33  , 34  , 35  , 36  , 37  , 38  , 39  , 40  , 41  , 42  , 43  , 44  , 45  , 46  , 47  , 48  , 49  , 50  , 51  , 52  , 53  , 54  , 55  , 56  , 57  , 58  , 59  , 60  , 61  , 62  , 63  , 64  , 65  , 66  , 67  , 68  , 69  , 70  , 71  , 72  , 73  , 74  , 75  , 76  , 77  , 78  , 79  , 80  , 81  , 82  , 83  , 84  , 85  , 86  , 87  , 88  , 89  , 90  , 91  , 92  , 93  , 94  , 95  , 96  , 97  , 98  , 99  ,
5
  99  , 98  , 97  , 96  , 95  , 94  , 93  , 92  , 91  , 90  , 89  , 88  , 87  , 86  , 85  , 84  , 83  , 82  , 81  , 80  , 79  , 78  , 77  , 76  , 75  , 74  , 73  , 72  , 71  , 70  , 69  , 68  , 67  , 66  , 65  , 64  , 63  , 62  , 61  , 60  , 59  , 58  , 57  , 56  , 55  , 54  , 53  , 52  , 51  , 50  , 49  , 48  , 47  , 46  , 45  , 44  , 43  , 42  , 41  , 40  , 39  , 38  , 37  , 36  , 35  , 34  , 33  , 32  , 31  , 30  , 29  , 28  , 27  , 26  , 25  , 24  , 23  , 22  , 21  , 20  , 19  , 18  , 17  , 16  , 15  , 14  , 13  , 12  , 11  , 10  , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0
6
};
7
8
ISR (TIMER1_OVF_vect)
9
{
10
  //Erzeugen des Signalverlaufs
11
  aktuellerWert = test[test2];
12
  if (test2 < 199)
13
    test2++;
14
  else
15
    test2 = 0;
16
17
  //Ausgeben des Signals
18
  OCR1A = (((periodendauer * aktuellerWert) / 100) * (prozent / 100));
19
}



Ich bin auch schon mit der Auslösezeit des Interrupts runtergegangen von 
anfangs 512 mal zu nur noch 200mal gebracht hat das aber auch nichts 
wirklich etwas

Falk B. schrieb:
> Wozu brauchst du 2 ISRs, die das gleiche machen?

Das Signal soll später wahlweise an dem einem oder dem anderen Pin 
anliegen können.

Falk B. schrieb:
> 5.) Du braucht hier keine Sekunde irgendwelche Floats, erst recht NICHT
> in einem hochfrequenten Interrupt!!! Das macht man mit
> Festkommaarithmetik, im einfachsten Fall mit 16 Bit, bei dem man
> nach der Multiplikation die unteren 8 Bit ignoriert!

Ich habe jetzt in meinen ISR die Berechnung mit Floats umgangen auch 
wenn ich es anders gemacht habe als du Vorgeschlagen hast könnte hier 
die Zeit verloren gehen?

von Falk B. (falk)


Lesenswert?

Roger S. schrieb:
> Zu meinem Programaufbau mit den Funktionen in header Dateien.
> Dass das nicht die schönste Art und Weise zu Programmieren ist ist mir
> bekannt aber für die Übersichtlichkeit finde ich es hilfreicher als
> nochmal das doppelte an Dateien rumfliegen zu haben.

Das ist kompletter Unsinn. Du produzierst das reinste Chaos!

> Meines Wissens hat
> das ganze auch keine Programmtechnischen Nachteile,

Aber sicher! Das hat es massenhaft.

> wenn ich aber das
> nächste mal hier ein Programm hochlade passe ich es aber an.

Gewöhn dir den Scheiß ab und mach es wie der Rest der Welt. Das klingt 
vielleicht uncool und nach Masse, ist aber totzdem 10x sinnvoller.

> Deine Vorschläge den Dateinamen immer zusammen zu bauen statt die haufen
> an Strings zu speichern werde ich umsetzen sobald die Signal Erzeugung
> funktioniert.

Das Eine hat mit dem anderen nix zu tun. Außerdem ist das deutlich 
einfacher und schneller gemacht.

> Ich habe versucht meine ISR Routine weiter aufzuräumen allerdings ohne
> merklichen Erfolg bei der Bearbeitungszeit.

Wie hast du das denn gemessen?

> ISR (TIMER1_OVF_vect)
> {
>   //Erzeugen des Signalverlaufs
>  if (richtung)
>   { //abwärts
>     if (aktuellerWert > 0)
>       aktuellerWert--;
>     else
>       richtung = 0;
>   }
>   else
>   { //aufwärts
>     if (aktuellerWert < 100)
>       aktuellerWert++;
>     else
>       richtung = 1;
>   }
>   //Ausgeben des Signals
>   OCR1A = (((periodendauer*aktuellerWert)/100)* (prozent /100));

Was soll das? So eine Rechung mit mehreren Multiplikationen und 
Divisionen braucht einiges an CPU-Leitung und ist unsinnig. Auch wenn es 
nur Integer sind. Und wenn gleich man das Dreiecksignal berechnen kann, 
wie du es tust, ist auch das nicht sinnvoll. Nimm eine Tabelle mit 256 
Einträgen, wo dein Signal schon vorausberechnet drin steht und gib die 
einfach stur aus, so wie ich es skizziert habe.

> Statt der if else Verschachtelung habe ich auch die Umsetzung mit einem
> Array versucht aus dem der aktuelle Wert geholt wird. Allerdings ohne
> Besserung der Bearbeitungszeit.

Woher weißt du das?

Das ganze sieht dann so aus:
> uint16_t test2 = 0;
> const uint8_t test[] = {
>   0  , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10  , 11  , 12  , 13  , 14  ,
> 15  , 16  , 17  , 18  , 19  , 20  , 21  , 22  , 23  , 24  , 25  , 26  ,
> 27  , 28  , 29  , 30  , 31  , 32  , 33  , 34  , 35  , 36  , 37  , 38  ,
> 39  , 40  , 41  , 42  , 43  , 44  , 45  , 46  , 47  , 48  , 49  , 50  ,
> 51  , 52  , 53  , 54  , 55  , 56  , 57  , 58  , 59  , 60  , 61  , 62  ,
> 63  , 64  , 65  , 66  , 67  , 68  , 69  , 70  , 71  , 72  , 73  , 74  ,
> 75  , 76  , 77  , 78  , 79  , 80  , 81  , 82  , 83  , 84  , 85  , 86  ,
> 87  , 88  , 89  , 90  , 91  , 92  , 93  , 94  , 95  , 96  , 97  , 98  ,
> 99  ,
>   99  , 98  , 97  , 96  , 95  , 94  , 93  , 92  , 91  , 90  , 89  , 88
> , 87  , 86  , 85  , 84  , 83  , 82  , 81  , 80  , 79  , 78  , 77  , 76
> , 75  , 74  , 73  , 72  , 71  , 70  , 69  , 68  , 67  , 66  , 65  , 64
> , 63  , 62  , 61  , 60  , 59  , 58  , 57  , 56  , 55  , 54  , 53  , 52
> , 51  , 50  , 49  , 48  , 47  , 46  , 45  , 44  , 43  , 42  , 41  , 40
> , 39  , 38  , 37  , 36  , 35  , 34  , 33  , 32  , 31  , 30  , 29  , 28
> , 27  , 26  , 25  , 24  , 23  , 22  , 21  , 20  , 19  , 18  , 17  , 16
> , 15  , 14  , 13  , 12  , 11  , 10  , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1
> , 0
> };
> ISR (TIMER1_OVF_vect)
> {
>   //Erzeugen des Signalverlaufs
>   aktuellerWert = test[test2];
>   if (test2 < 199)
>     test2++;
>   else
>     test2 = 0;
>   //Ausgeben des Signals
>   OCR1A = (((periodendauer * aktuellerWert) / 100) * (prozent / 100));
> }

Schon wieder die sinnlose Formel. Lies mal was über 
Festkommaarithmetik.
Und warum eine Tabelle mit 200 Einträgen? Ja, kann man machen, bringt 
aber wenig Vorteile.

> Ich bin auch schon mit der Auslösezeit des Interrupts runtergegangen von
> anfangs 512 mal

Was für ein Ding? Was heißt denn 512 mal? 512 mal pro Sekunde? Das ist 
GAR NICHTS für so eine CPU.

> zu nur noch 200mal gebracht hat das aber auch nichts
> wirklich etwas

Wie hast du das festgestellt?

>
> Falk B. schrieb:
>> Wozu brauchst du 2 ISRs, die das gleiche machen?
>
> Das Signal soll später wahlweise an dem einem oder dem anderen Pin
> anliegen können.

Das macht man trotzdem nicht so, nur in SEHR besonderen Fällen. Hier 
brauchst du keine verschiedenen Frequenzen für die ISR, also können 
beide Zähler mit der gleichen Frequenz laufen. Dann reicht es, die 
Berechnungung der neuen Daten in einer ISR zum machen. Das spart massiv 
CPU-Leistung.

> Ich habe jetzt in meinen ISR die Berechnung mit Floats umgangen

Toll, dafür ein halbes Dutzend Multiplikationen und Divisionen 
reingebracht, wie ein total naiver Anfänger 8-0

auch
> wenn ich es anders gemacht habe als du Vorgeschlagen hast könnte hier
> die Zeit verloren gehen?

Ach herje . . . . .
Wir haben doch alle zeit der Welt, wie soll da was verloren gehen?

von Falk B. (falk)


Lesenswert?

Das mit der Amplitude macht man so.
1
OCR1A=(pgm_read_byte(&waveform[i>>8]) * amplitude ) >> 8;

Die Schiebeoperationen sind sehr schnell, denn der Compiler merkt, daß 
er einfach nur das LSB wegwerfen muss und nur mit dem MSB rechnen kann. 
Am Ende ist es nur eine echte Multiplikation, die dank 
Multiplikationsbefehl nur wenige Takte benötigt.

Amplitude ist auf 256 normiert, sprich 256 == 1 == 100%.
Halt Festkommaarithmetk. Das Zahlenformat wäre I0Q8,
sprich 0 Bits für die Vorkommastellen, 8 Bits Nachkomma, kein 
Vorzeichen.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Ahhhhhh, jetzt sehe ich es!

Was soll das?
1
  TIMSK5 |= (1 << OCIE5A);
2
  sei();

Du gibst neben den beiden Interrupts für Timer 1 und 4 auch den für 
Timer 5 frei. Dafür gibt es aber keine ISR! Das heißt aber, daß dadurch 
ein "Bad ISR vector" ausgelöst wird, was beim AVR zu einem Reset führt. 
Dein Programm stürzt damit dauerhaft ab!

MAN DARF NIEMALS EINEN INTERRUPT FREIGEBEN, FÜR DEN ES KEINE ISR GIBT!

Was zum Geier machst du mit DREI Timern? Nur weil der Mega tonnenweise 
davon hat, muss man die nicht so sinnlos einsetzen.

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.