Forum: Compiler & IDEs zwei if-Anweisungeg funktionieren nicht zusammen


von Rachid E. (rachid_e)


Angehängte Dateien:

Lesenswert?

Hallo Leute,

ich brauche wieeder mal wieder eure Hilfe. In meinem Programm 
funktioniert alles einwandfrei, bis auf ein paar Kleinigkeiten. 
Vermutlich sehe ich den Wald vor lauter Bäumen nicht.
In meinem Code habe ich die betroffenen Stellen markiert. Mein Programm 
erstellt drei PWM-Signale. Außerdem beinhaltet der Code zwei Schleifen, 
die jeweils eine Variable hoch zählen. Position eins und zwei markieren 
die Stellen. Mit der Variable möchte ich ein Array abfragen und damit 
das duty cycle in position 2 aktualisieren.

Probleme:

1. Die Variable tem zählt nur bis 64

2. Die zweite Schleife zählt gar nicht

Meine Tests:

Ich habe die if-Anweisungen in verschiedenen Schreibweisen ausprobiert. 
Auch in verschachtelter Schreibweise und es hat dennoch nicht 
funktioniert.
Getestet habe ich es mit einem Oszilloskop. Der Speicher vom Controller 
ist auch nicht voll.

Ich wäre euch sehr dankbar für ein paar Tipps.

Viele Grüße,

Rachid

von (prx) A. K. (prx)


Lesenswert?

Der Anhang ist praktisch unlesbar. Probiers mal ohne Tabs, der einzige 
Tab-Standard ist 8 (Unix), deiner wohl 2 oder so, und entsprechend 
grauslich sieht es aus.

Warum .txt für .c? Um Andreas' Formatierer auszuhebeln?

von J.-u. G. (juwe)


Lesenswert?

Rachid E. schrieb:
> 1. Die Variable tem zählt nur bis 64

Wieso "nur"?

Rachid E. schrieb:
> Getestet habe ich es mit einem Oszilloskop.

Kanns Du kurz erklären, wie Du hast mit einem Oszi den Inhalt der 
Variable tem gelesen hast?

von Peter II (Gast)


Lesenswert?

das sieht schon mal nicht gut aus:

#define PI 3,41


da passiert bestimmt nicht das was du erwartest.

von Rachid E. (Gast)


Lesenswert?

Danke für eure Antwort. Ich bin jetzt nicht mehr im Labor. Ich werde die 
Frage sobald wie möglich verständlicher stellen. Testen kann ich aber 
leider erst morgen, da bis dahin das Labor geschlossen ist.

von Karl H. (kbuchegg)


Lesenswert?

Und ehe du ein neues C-File postest, mach es mit Notepad auf, schau dir 
die Formatierung an und korrigiere sie. So was wie da oben hat nur ganz 
geringe Chance, dass irgendwer auch nur den Versuch machen wird da 
durchzusteigen.

von Peter D. (peda)


Lesenswert?

Karl Heinz Buchegger schrieb:
> So was wie da oben hat nur ganz
> geringe Chance, dass irgendwer auch nur den Versuch machen wird da
> durchzusteigen.

Stimmt.
Eine Zeilenlänge von 1000 Zeichen können die wenigsten darstellen.
Gebräuchlich sind 80 .. 120 Zeichen.
Tabellen sollte man auf 10 oder 16 Werte je Zeile aufteilen.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Da fällt mir gerade ein, dass ich in diesem Thread

Beitrag "Re: Anfängerhilfe bei Input"

ein paar Tips gegeben haben (nur das allernotwendigste), die bei einer 
vernünftigen Formatierung hilfreich sind.

Vergiss die Fragestellung und lies dir die 'Einführung' in Formatierung 
durch. Das ist zwar nicht alles was es zu diesem Thema zu sagen gibt, 
aber es ist schon mal ein erster brauchbarer Grundstock.

von Rachid E. (rachid_e)


Angehängte Dateien:

Lesenswert?

Danke für eure Komentare. Ich habe versucht, alles einzubeziehen. Ich 
hoffe man kann jetzt was damit anfangen.

Noch kurz zum Programm:

Ich möchte mit dem Programm einen dreiphasigen Gleichstrommotor 
ansteuern. Alles geht bis auf die zwei if-Anweisungen in der 
while-Schleife.

    if (tem>=60)
    {
      tem=0;
      ii=ii+1;
    }

    if (ii>=20)
    ii=0;

Um zu testen, habe ich die Variablen tem und ii und sin[ii] als 
Vergleichswert für die duty cycle Einstellung genutzt. Dadurch müsste 
sich das PWM-Signal ständig ändern. Mit der Variable tem geht es 
einwandfrei. Doch mit ii und sin[ii] ändert sich gar nichts. Die 
Änderungen habe ich am Oszilloskop dargestellt.
Ein weitere Sache die ich nicht verstehe ist, dass sobald ich eine Zahl 
über 64 in der if-Anweisung schreibe, zählt die erste if-Anweisung auch 
nicht.

Ich habe bereits int, long, double und float ausprobiert. Nichts hat 
geholfen.

von Klaus W. (mfgkw)


Lesenswert?

Ich verstehe zwar deine Fehlerbeschreibung nicht, sage aber trotzdem 
auch was dazu:


1. C lernt man am besten nicht auf einem Controller und mit einer 
Problemstellung, die man kaum überblickt, sondern besser auf einem PC 
mit anfangs kleineren Aufgaben, die stückweise wachsen.
Das spart viel Frust.
Ein Debugger ist effizienter als ein Oszilloskop.


2. Ich stehe auch oft an einem Punkt, wo irgendwie nichts mehr
funktioniert und man nicht mehr weiß, warum.
Spätestens dann muß man sich eingestehen, daß man sich mit der
Komplexität übernommen hat (ja, das passiert jedem) und sein Problem
endlich aufteilen auf kleinere, lösbare Probleme.
Das wäre bei dir vielleicht:
- Ausgeben des PWM-Wertes
- Timer in den Griff bekommen
- AD-Werte einlesen
- PID-Regelung machen
- ...

Für jedes dieser Teilprobleme braucht man (erst für sich selbst,
und später ebenso für andere) eine klare Vorstellung, worum es geht,
was man zur Verfügung hat, was man möchte, und wie man dahin kommt.

Davon erledigt man eines nach dem anderen.

Die meisten Fehler tauchen in solchen kleinen, übersichtlichen
Programmen bereits auf und lassen sich dort erschlagen.
Die meisten solcher kleinen Probleme programmiert man auf dem PC,
wo man wesentlich besser debuggen kann und viel schneller die
Lösung hat.

Erst wenn diese Häppchen getestet sind und sicher funktionieren,
schraubt man daraus stückweise ein größeres Programm zusammen.

Nebenbei kommt man dann zu einem modularisierten Programm, in dem
nicht alles vermatscht ist (AVR-Register mitten in der
PID-Regelung?)
und dessen Teile man später auch wiederverwenden kann.

Wenn bei solchen Häppchen Probleme auftauchen, die man nicht
lösen kann, kann man sinnvollerweise andere um Hilfe fragen.
Ein Forum ist weniger hilfreich, wenn man als Nichtschwimmer in
einem Fluß badet, in dem sich Krokodile und Nilpferde tummeln,
Tausende Bakterien mutieren, von oben vollgekokste 12-Jährige
mit der AK-47 herumballern, kein Handy greifbar und das
Taschenmesser weg ist.


3. Wenn man in einem Forum Hilfe will, muß man den anderen eine
Chance geben, den Fehler nachvollziehen zu können.
Das heißt:
- den Fehler auf ein möglichst kleines, nachvollziehbares Beispiel
  reduzieren
- klar beschreiben, wie es sich verhalten sollte
- klar beschreiben, was falsch läuft (schön, wenn du weißt, was
  dir dein Oszilloskop zeigt; meines hat mit deinem Quelltext
  keine Probleme!)

Dagegen bitte nicht:
- solange ein Programm sinnlos vollstopfen, bis man es nicht mehr
  versteht (falls es jemals besser war)
- den ganzen unlesbaren Haufen in ein Forum schieben ohne
  vernünftige Beschreibung
- "geht nicht"
- "helft mir"

Damit verschwendet man anderer Leute Zeit.

Was schert mich der ganze Regelkram und Timerzeugs, wenn es um
z.B. die Ausgabe eines Wertes geht?


4. Die Formatierung und Beschreibung ist schon deutlich besser
geworden, auch wenn es immer noch mühsam ist, einen Sinn im
Quelltext zu entdecken.

Wieso soll tem immer bis 60 gehen und dann bei 0 anfangen?
Dto. ii bis 20?
Gibt es keine aussagekräftigeren Namen als tem und ii?


5. Für Sachen wie "i = i + 1" gibt es in C gängige Abkürzungen ("i++").
Das macht ein Programm etwas übersichtlicher.


6. Wieso sollte man einen double-Wert aus sin[], der bis 600 geht,
an ein 8-Bit-Register zuweisen?


7. Das macht wenig Sinn:
1
    duty=75,75;    //ist der Wert für das duty cycle. Wird später mit dem PID-Regler geregelt.
Vielleicht meintest du eine Gleitkommazahl (75.75 mit einem Punkt).
Aber wieso gerade 75.75?
Und warum an eine ganzzahlige Variable eine Gleitkommazahl zuweisen?


8. Es ist sinnvoll, mit allen verfügbaren Compilerwarnungen
übersetzen zu lassen, und sich um die Warnungen auch zu kümmern.
Der Punkt 7 wäre dir dann schon selbst aufgefallen, damit müsstest
du nicht andere Leute belästigen.


9. Man sollte dringend Sachen auseinanderhalten, die nicht
zusammengehören.
Wenn in einer Schleife irgendwas geregelt wird, gehören da keine
Zugriffe auf irgendwelche Register rein.
Eher sollte man zum Setzen einer Stellgröße eine Funktion aufrufen, in
der dann das HW-Gefummel gemacht wird.
Ein PID-Regler in Hochsprache hat nichts mit OCRirgendwas zu tun.


10. Was um alles in der Welt haben deine sin-Werte mit einem Sinus zu 
tun?

von Karl H. (kbuchegg)


Lesenswert?

Und nicht zuletzt: schmeiss alles raus, was nichts mit dem Programm zu 
tun hat.


Dein Programm macht

1
int main()
2
{
3
  Hardware initialisieren
4
5
  while( 1 ) {
6
7
    ADC auslesen
8
9
    ADC Wert auf 3 PWM ausgeben
10
  }
11
}

dazu braucht es keine Funktion rechtskom, dazu braucht es keinen PID 
Regler, dazu braucht es 60% deines Codes überhaupt nicht. Und den ADC 
würde ich erst mal mit den Tutorial Funktion auslesen und nicht im im 
Free-Running Modus.

von Klaus W. (mfgkw)


Lesenswert?

äh, hatte ich das noch vergessen? :-)

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:
> äh, hatte ich das noch vergessen? :-)

Ähm. Weiß nicht so genau :-) Ich denke du hast nicht.

Ich wollte nur nicht, dass das unter geht bei all den anderen 
berechtigten Punkten, die du angemerkt hast.


Das ganze sieht mir nämlich wieder mal nach dem leidigen Problem aller 
Einsteiger aus: Ich hab im vorletzten Übungsbeispiel einen Code 
produziert und den behalte ich unter allen Umständen im Programm. Den 
braucht zwar keiner, aber rauslöschen werd ich ihn auf keinen Fall.

von Karl H. (kbuchegg)


Lesenswert?

Bin ich blind?

Da ist doch ein ADC Interrupt freigegeben, für den er keine ISR hat.
Oder nicht?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Den
> braucht zwar keiner, aber rauslöschen werd ich ihn auf keinen Fall.

Wäre ja auch noch schöner, extra ein VCS aufzusetzen, damit sich
das an den alten Code erinnert. ;-)

von Karl H. (kbuchegg)


Lesenswert?

OK. Rachid.
Mal etwas konkreter

So was
1
  double sin[61]={0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 
2
    280, 290, 300, 310, 320, 330, 340, 350, 360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 
3
    540, 550, 560, 570, 580, 590, 600};

willst du nicht machen.

Über den double brauchen wir nicht lange debattieren. Das der Unsinn 
ist, siehst du wohl selber.

Es geht um die 61.
Solche magische Zahlen willst du nicht im Code haben. Die Gefahr ist 
viel zu groß, dass du dich verzählst oder +-1 verhaust. Ausserdem ist 
das mühsam abzuzählen.

Denn: Das kann der Compiler auch für dich erledigen!

Und wenn du das dann auch noch ein wenig nett in Tabellenform anordnest, 
dann kann man auch auf einen Blick erkennen, dass das eine lineare Rampe 
ist und auch wieviele Werte das sind.

1
  uint8_t sin[] = {   0,  10,  20,  30,  40,  50,  60,  70,  80,  90,
2
                    100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
3
                    200, 210, 220, 230, 240, 250, 260, 270, 280, 290,
4
                    300, 310, 320, 330, 340, 350, 360, 370, 380, 390,
5
                    400, 410, 420, 430, 440, 450, 460, 470, 480, 490,
6
                    500, 510, 520, 530, 540, 550, 560, 570, 580, 590,
7
                    600
8
                  };

So.
Jetzt zählt der Compiler für dich die Zahlen ab und dimensioniert das 
Array entsprechend gross. Fügst du einen Wert hinzu, macht der Compiler 
das Array automatisch größer, nimmst du einen Wert raus, macht er es 
automatisch kleiner.

Nur: WOher weiß jetzt dein Programm, wieviele Werte das sind?
Bisher hast du ja einfach 60 bzw. 61 in den Programmtext geschrieben. 
Wenn der Compiler die Größe des Arrays festlegt, sind es ja keine 61 
Werte mehr. Das müsste man daher anpassen.

Auch das kann dein Compiler für dich erledigen!

In C gibt es den sizeof() Operator. Der bestimmt die Größe in Bytes von 
einer Variablen.

sizeof( sin )   gibt dir daher die Größe in Bytes, die das komplette 
Array verbraucht. Dividiert man das durch die Größe von einem einzelnen 
Arrayelement, dann erhält man logischerweise die Anzahl an Elementen. 
Denn die komplette Größe errechnet sich ja durch

       komplette_Größe =   Anzahl_Einträge * Größe eines Eintrags

ein bischen Formel Umstellen und die Behauptung folgt daraus.

Das kann man aber auch in C schreiben

  uint8_t nrSin = sizeof( sin ) / sizeof( sin[0] );

und schon hast du alles was du brauchst um diese magische Zahl aus dem 
Programm zu verbannen.
1
int main(void)
2
{
3
  uint8_t sin[] = {   0,  10,  20,  30,  40,  50,  60,  70,  80,  90,
4
                    100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
5
                    200, 210, 220, 230, 240, 250, 260, 270, 280, 290,
6
                    300, 310, 320, 330, 340, 350, 360, 370, 380, 390,
7
                    400, 410, 420, 430, 440, 450, 460, 470, 480, 490,
8
                    500, 510, 520, 530, 540, 550, 560, 570, 580, 590,
9
                    600
10
                  };
11
  uint8_t nrSin = sizeof( sin ) / sizeof( sin[0] );
12
  uint16_t adx;
13
14
  DDRD = 0xFF;  
15
  //DDRA = 0x00;
16
  DDRB = 0xFF;
17
  DDRC = 0x2;  //OC1B
18
  DDRE = 0x2;  //OC0B
19
20
  ad_conver();     //Aufruf der Funktion zur initialisierung des AD.Wandlers
21
22
  timer_init();    //Aufruf der Funktion zur initialisierung der Timer
23
24
  sei();
25
26
  int tem, ii;
27
  tem=0;
28
  ii=0;
29
30
  while(1)
31
  {
32
    tem++;
33
    if( tem >= nrSin - 1 )      
34
    {            
35
      tem = 0;        
36
      ii = ii + 1;      
37
    }            
38
39
    if( ii >= 20 )        
40
      ii = 0;          
41
42
    adx = ADCW;
43
44
    OCR0B = sin[ii];    // Vergleichswert für die Duty Cycle Einstellung  
45
    OCR1B = adx;        // Vergleichswert für die Duty Cycle Einstellung  
46
    OCR1A = ii;         // Vergleichswert für die Duty Cycle Einstellung  
47
  } 
48
49
  return 0;
50
}

Im übrigen denke ich nicht, dass du da an der PWM großartig was sehen 
wirst.

Zu deinen Kommentaren:
1
  ad_conver();     //Aufruf der Funktion zur initialisierung des AD.Wandlers
2
3
  timer_init();    //Aufruf der Funktion zur initialisierung der Timer
Gut dass du mir das alles im Kommentar erzählst. Da wär ich von alleine 
nie drauf gekommen, dass timer_init()
* der Aufruf einer Funktion ist
* und dass diese Funktion höchst wahrscheinlich den Timer
  initialisieren wird.

Aber: Wenn du dir bei ad_conver() dazuschreiben musst, dass das die 
Funktion ist, die den ADC initisliert, dann solltest du soweit denken, 
dass du dich fragst: wie müsste ich die Funktion nennen, damit ich eben 
keinen Kommentar brauche!

Nenn die Funktion adc_init() und jedem ist klar, was dieser 
Funktionsaufruf bewirkt.

Und so gibt es noch einige andere Kommentare in deinem Programm, die 
reichlich sinnlos sind. Kommentiere nicht das, was ohnehin im 
Programmtext steht! Du musst das kommentieren was keiner wissen kann, 
wenn er sich nur den Programmtext ansieht! Kommentiere das "Warum" und 
nicht das "Wie"! Das "Wie" steht im Code, aber das "Warum" existiert nur 
in deinem Kopf. Also schreib es hin!

Benenne Variablen nach ihrer Bedeutung! Ein Variablenname tim oder ii 
erzählt mir nichts.

Wenn in einem Programm steht

     xyz = i + i * u;

dann sagt das einem Leser gar nichts. Wenn da aber steht

     verkaufsPreis = nettoPreis + nettoPreis * Mehrwertsteuer;

dann erzählt mir diese Anweisung etwas! Da wird zu einem Preis die 
Steuer berechnet und draufgeschlagen um den Preis zu erhalten um den 
etwas verkauft wird. Da muss ich den Rest des Programmes gar nicht sehen 
um zu wissen, dass bei

     verkaufsPreis = nettoPreis - nettoPreis * Mehrwertsteuer;

etwas nicht stimmen kann und was da nicht stimmen kann.

Sorge immer dafür, dass dein 'Hauptkommentar' eines Programmes das 
Programm selber ist! Dies macht man mit gut gewählten Funktionsnamen. 
Dies macht man mit gut gewählten Variablennamen. Überleg dir bei jedem 
Kommentar, den du hinschreiben willst, ob es nicht eine Möglickeit gibt 
den Code so zu verändern, dass du den Kommentar NICHT brauchst. Denn der 
beste Kommentar ist jener, den du nicht hinschreiben musst!

So ein Kommentar

  int tem, ii; // Variablen für die if-Anweisung
  tem=0;
  ii=0;


ist komplett sinnlos. Was soll das sein "Variablen für die 
if-Anweisung"? Was sagt mir das über die Bedeutung der Variablen? Was 
repräsentieren sie? Richtig: Das sagt mir gar nichts. Und spätestens 
wenn dann ein zweites if ins Program kommt, dann stimmt der Kommentar 
sowieso schon nicht mehr.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> uint8_t sin[] = { ..., 600 ... }


das wird aber sehr eng mit dem uint8_t

von Klaus W. (mfgkw)


Lesenswert?

Daher lieber double :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> uint8_t sin[]

Jetzt sollte man aber noch den Namen ändern.  sin ist ein
reservierter Bezeichner, denn es ist eine mathematische Funktion
der Standardbibliothek.

"ramp" hätte deutlich mehr Sin(n) als Name hier. ;)

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:
> Daher lieber double :-)

:-)

Können wir uns auf uint16_t einigen?

von Klaus W. (mfgkw)


Lesenswert?

na gut, zur Not sollte das gehen.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Können wir uns auf uint16_t einigen?

ja, da es leider kein uint10_t gibt.

von J.-u. G. (juwe)


Lesenswert?

Man kann ja auch alle Werte vor dem Speichern durch 10 teilen. Damit 
verliert man keine Information (zumindest im Beispiel von Karl Heinz) 
und kann doch wieder uint8_t nehmen. :)

von Werner B. (werner-b)


Lesenswert?

Das ganze Array ist Quatsch..

Statt
  OCR0B = sin[ii];    // Vergleichswert für die Duty Cycle Einstellung
einfach
  OCR0B = 10 * ii;    // Vergleichswert für die Duty Cycle Einstellung
und das Array in die Tonne kloppen.

Wobei das auch nicht klappen wird. Denn bei allen AVRs (außer den Xmega) 
die ich kenne ist TIMER0 ein 8 Bit Zähler. Also ist bei 255 Schluss.

P.S. Obwohl der Controller nicht genannt wird kann man aus den 
Registernamen schließen, daß es sich um Modell der MegaAVR Reihe handeln 
muss.

von Rachid E. (rachid_e)


Angehängte Dateien:

Lesenswert?

So, jetzt hab ich es geschaft alles nochmal umzuändern. Ich will mich 
besonders für den Tip mit dem Array bedanken. Das ersparrt wirklich viel 
Mühe. Ich hab jetzt versucht alles so gut es geht zu verbessern.

Ich habe meinen Programm in einzelnenTeilen gehackt und die Teile 
einzeln getestet. Die Timer, der AD-Wandler und die if_Anweisungen 
funtionieren alleine ganz gut. Wenn alles wieder eingesetzt wird, kommt 
das gleiche Problem. Als vergleichswert für die PWMs werden die 
Variablen a,b und x[b ] (neu benannt) jeweils eingesetzt. Am Oszilloskop 
wird getestet wie sich das PWM-Signal verändert. Das PWM-Signal hat sich 
gar nicht geändert.

Wird vom Programm der AD-Wandler entfernt, funktioniert alles 
einwandfrei. Wenn ich so die PWMs am Oszilloskop darstelle, sieht man 
eine kontinuierliche Veränderung des duty cycles.

Ich hoffe ich hab jetzt nichts vergessen. Der µC den ich benutze ist der 
AT90PWM316.

von Karl H. (kbuchegg)


Lesenswert?

Rachid E. schrieb:
> So, jetzt hab ich es geschaft alles nochmal umzuändern.

Sieht schon besser aus, auch wenn ich mit a und b noch nicht glücklich 
bin.


Aber das hier

  ADCSRA |= (1<<ADIE);              //Interrupt erlauben


muss raus!

Du hast keine ISR für diesen Interrupt

von Rachid E. (rachid_e)


Lesenswert?

Super, ich danke dir. Jetzt geht alles eiwandfrei. Dass so ein kleiner 
fehler schon so viel anstellen kann, machts einem nicht einfach.
Ich bin hier aber um eine Menge schlauer geworden. Und dafür noch ein 
großes Danke, an alle die geholfen haben.

Ich wünsche Euch allen noch ein schönes Wochenende.

Viele Grüße,

Rachid

von Karl H. (kbuchegg)


Lesenswert?

Rachid E. schrieb:
> Super, ich danke dir. Jetzt geht alles eiwandfrei. Dass so ein kleiner
> fehler schon so viel anstellen kann, machts einem nicht einfach.

Lass mich raten.
Du hast die ADC initialisierung einfach wo herkopiert.

Das mindeste was man in so einem Fall immer machen muss: Prüfen ob 
irgendwo Interrupts freigegeben werden! Daher ist es auch nicht so 
schlau, wenn man sich den Code so schreibt, dass man das möglichst 
schlecht sieht. Ein freigegebener Interrupt ist eine wichtige Sache! Die 
soll nach Möglichkeit rufen: "Hier, bitte ich!"

von Rachid E. (rachid_e)


Lesenswert?

Ich habe die Initialisierung schon selber gemacht. Ich habe bis jetzt 
blos noch nie einen Interrupt benutzt und wusste halt nicht dass es so 
viel ausmacht wenn man ihn enabelt und nicht nutzt.
Mir kam am Anfang nicht der Gedanke, dass irgend etwas am AD-Wandler 
falsch sein könnte. Der AD-Wandler hatte ja richtig funktioniert. Die 
Wirkung war halt ganz wo anders.

von Rachid E. (rachid_e)


Lesenswert?

Da ist doch noch ein Fehler drinne. Wenn ich

         ADCSRA |= (1<<ADIE);              //Interrupt erlauben

raus nehme oder auf Null setzte, macht der AD-Wandler nur eine Messung.
Das ist mir vorhin nicht eingefallen, da ich nur auf die if-Anweisungen 
und der PWMs geachtet hatte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rachid E. schrieb:
> Da ist doch noch ein Fehler drinne. Wenn ich
>
>          ADCSRA |= (1<<ADIE);              //Interrupt erlauben
>
> raus nehme oder auf Null setzte, macht der AD-Wandler nur eine Messung.

Du hast den auto trigger Modos (ADATE = 1) aktiv, nun musst du aber
mal konkreter werden, welcher Controllertyp das ist, damit man sich
die Triggerquelle nochmal ansehen kann.

Übrigens:
1
  ADMUX |=  ((0<<REFS1) | (0<<REFS0));
2
  ADMUX |=  ((0<<MUX3) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0));
3
  ADCSRA |= (1<<ADEN);
4
  ADCSRA |= (1<<ADATE);
5
  ADCSRA |= (1<<ADIF);
6
  ADCSRA |= (1<<ADIE);
7
  ADCSRA |= ((1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0));
8
  ADCSRB |= ((0<<ADTS3) | (1<<ADTS2) | (0<<ADTS1) | (0<<ADTS0));
9
  ADCSRA |= (1<<ADSC);

Das ist reichlich viel Code.  Allein die ersten beiden Zeilen sind
zwei aufwändige no-ops.  Der Compiler darf diese aber nicht weg
optimieren (weil ja auf IO-Registern gearbeitet wird), es wird also
jedesmal ADMUX gelesen und wieder ausgegeben.  Schreib' das besser
jeweils in ein statement, und nicht als read-modify-write, sondern
als absolute Zuweisung:
1
  /* ADMUX = 0; */  /* keine Änderung nötig, kann weggelassen werden */
2
  ADCSRB = /* no ADTS3 */ (1<<ADTS2) /* no ADTS1, no ADTS0 */;
3
  ADCSRA = (1<<ADEN) /* ADC einschalten */ |
4
           (1<<ADATE) /* auto-trigger-Quelle Timer xx? */ |
5
           (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) /* clk/128 => 7.8 kHz */;

ADSC kann man mit read-modify-write setzen, ist aber eigentlich
überflüssig, da du ja ohnehin einen Autotrigger haben willst.
1
//#define TIMER0_OVF_vect

Ganz löschen.  Hat keinen Sinn, wenn man das Kommentarzeichen
entfernen würde.

von Rachid E. (rachid_e)


Lesenswert?

Danke fü die Tips. Ich kann jetzt leider nichts mehr testen bis Montag, 
da das Labor bis dahin geschlossen ist. Als Controller benutze ich den 
AT90PWM316 und das Board Stk500 mit das Ergänzungsboard Stk520. Beim 
AD-Wandler versuche ich blos nur eine kontinuierliche Messung zu 
erreichen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rachid E. schrieb:
> Als Controller benutze ich den
> AT90PWM316

Dort entspricht der Wert ADTS[3:0] = 0b0100 (den du programmierst)
der Triggerquelle "Timer/Counter 0 Overflow".  Du musst also den
Timer 0 so programmieren, dass sein Überlauf der von dir gewünschte
ADC-Trigger-Rate entspricht.  Du schreibst nicht, mit welcher
Taktfrequenz der Controller läuft, standardmäßig ist es 1 MHz.
Mit dem von dir gewählten ADC-Prescaler von 128 wird der ADC dann
schon ziemlich langsam, eine Wandlung dauert nach dem Anstoßen
stolze 2 ms.  Vielleicht überdenkst du auch den Prescalerwert
nochmal.

von Rachid E. (rachid_e)


Lesenswert?

Timer0 wird bei mir schon genutzt um ein drittes PWM-Signal zu erzeugen. 
Ich weis nicht ob ich den timer0 gleichzeitig auch dafür benutzen kann. 
Als Taktgeber benutze ich den internen Quarz. Wenn ich mich nicht irre 
hat der 8 MHz. Den Adc-prescaller werde ich dann ändern.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rachid E. schrieb:
> Timer0 wird bei mir schon genutzt um ein drittes PWM-Signal zu erzeugen.
> Ich weis nicht ob ich den timer0 gleichzeitig auch dafür benutzen kann.

Ja, natürlich.

Vielleicht postest du ja dann auch mal den kompletten Code.

> Als Taktgeber benutze ich den internen Quarz. Wenn ich mich nicht irre
> hat der 8 MHz.

Wenn du die CKDIV8-Fuse nicht geändert hast, wird das trotzdem auf
1 MHz runtergeteilt.

von Klaus W. (mfgkw)


Lesenswert?

Nur am Rande: es gibt keinen internen Quarz, nur einen internen 
Oszillator.

von Rachid E. (rachid_e)


Angehängte Dateien:

Lesenswert?

Ich hab jetzt den ganzen Code hochgeladen. Wie schon geschrieben 
funktioniert der AD-Wandler nicht ganz. Die if-Anweisungen 
Funktionieren. Nur der AD-Wandler wandelt nur ein mal, beim Start des 
Programms.

Ich möchte die höchstmögliche frequenz aus dem Controller haben. Einen 
externe Quarz kann ich leider nicht benutzen, da der Quartzeingang 
gleichzeitig der Ausgang für das PWM-Signal von Timer0 ist.
Ich bin euch auch sehr Dankbar wenn ihr mir einen Tip gibt, ob oder wie 
ich eine Höhere Frequenz einstellen kann. Ich habe bereits den TOP-Wert 
der Timer auf 100 runtergesetzt, damit ich meine jetzige 10kHz bekomme.

Den ADC-Prescaler habe ich schon korrigiert. Ich habe auch den ADTS 
verändert und leider ohne erfolg getestet.


PS: Ich hab jetzt den Hacken von CKDIV8 in der Einstellung entfernt. 
Jetzt habe ich eine Frequenz von 80kHz. Das ist nun mehr als ich 
brauche. Danke für den Tip.

von Karl H. (kbuchegg)


Lesenswert?

Rachid E. schrieb:
> Ich hab jetzt den ganzen Code hochgeladen. Wie schon geschrieben
> funktioniert der AD-Wandler nicht ganz. Die if-Anweisungen
> Funktionieren. Nur der AD-Wandler wandelt nur ein mal, beim Start des
> Programms.

Logisch. Du hast ja auch den Freerunnnig MOdus wieder abgeschaltet.

Tu dir doch selber einen Gefallen und benutze erst mal die ADC Routinen 
aus dem AVR-GCC-Tutorial.

>
> Ich möchte die höchstmögliche frequenz aus dem Controller haben.

Ja, ja. Das wollen sie alle,
Du hast noch einen langen Weg vor dir und im Endeffekt wird das 
endgültige Programm mit dem jetzigen nicht mehr viel gemeinsam haben. 
Das macht man nämlich alles ganz anders :-)
Vor allen Dingen macht man die Zeitsteuerung autonom mit einem Timer. 
Das Stischwort lautet DDS.
Das hier
1
  
2
    /* Die if-Anweisungen stellen die Geschwindigkeit, mit der sich der Vergleichswert für die PWMs ändert
3
    Es sind zwei if-Anweisungen eingesetzt, damit kein zu hoher Zählwert in einer if-Anweisung eingesetzt werden muss*/
4
    a=a+1;        
5
    if (a>=600)      
6
    {            
7
      a=0;        
8
      b++;      
9
    }            
10
  
11
    if (b>=nrx)        
12
    b=0;

ist doch Murks erster Güte.

Und das hier
1
    c=adx*0.1;
ist schon mal ein kompletter Widerspruch zu der Forderung: so schnell 
wie möglich.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rachid E. schrieb:
> Ich hab jetzt den ganzen Code hochgeladen. Wie schon geschrieben
> funktioniert der AD-Wandler nicht ganz.

Genauer: es lässt sich nichtmal compilieren:

% avr-gcc -Os -mmcu=at90pwm316 -o Motor1.elf Motor1.c
Motor1.c:97:11: error: invalid suffix "b" on integer constant

Tu dir und uns einen Gefallen, und lade hier doch genau das hin, mit
dem du auch selbst experimentierst, und nicht eine irgendwie zurecht-
gestutzte Version.  Andernfalls suchen wir andere Fehler als du.

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.