Forum: Mikrocontroller und Digitale Elektronik Erhöhen der Poti-Spannung führt zu undefiniertem Verhalten


von Daniel (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

über ein Potentiometer kann die Lage der Zündimpulse verstellt werden. 
Bei schnellem Drehen des Potis treten Kurzschlüsse, siehe Bild, zwischen 
den Zündimpulsen auf, allerdings nur, wenn man die Zündimpulse nach 
links verschiebt, also die Spannung am Poti vergrößert. Beim Verschieben 
der Zündimpulse nach rechts gibt es keinerlei Probleme, unabhängig wie 
schnell man am Poti dreht.

Kann mir jemand sagen wie man an dieses Problem herangeht?
Mittelwertbildung des ADC-Ergebnisses hab ich schon versucht, das 
Problem bleibt aber bestehen.

1
int mittelwert(void)
2
{
3
  int anzahl = 100;
4
  double summe = 0;
5
  int ADC_Tab[anzahl];
6
7
  for(int x = 0; x < anzahl; x++)
8
  {
9
    ADC_Tab[x] = ADC_Lesen();
10
  }
11
12
  for(int i = 0; i < anzahl; i++)
13
  {
14
    summe += ADC_Tab[i];
15
  }
16
17
  return (summe / anzahl);
18
}
19
20
void AdcInit(void)
21
{
22
  ADMUX |= (0<<MUX0) | (0<<MUX1) | (0<<MUX2);   
23
  ADMUX &= ~((1<<REFS0) | (1<<REFS1));
24
  ADMUX |=(0<<ADLAR);                
25
  ADCSRA|=(1<<ADSC);               
26
  ADCSRA |=(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);  
27
}
28
29
uint16_t ADC_Lesen(void)
30
{
31
  uint16_t result = 0;
32
33
  ADCSRA |=(1<<ADEN);    
34
  ADCSRA |= (1<<ADSC);           
35
  while (ADCSRA & (1<<ADSC));
36
37
  ADCSRA &= ~(1<<ADEN);
38
  result = ADCW; 
39
  return result;               
40
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Dein Problem ist höchst wahrscheinlich nicht die Ermittlung der Spannung 
(und damit der Potistellung), sondern wie du daraus die Zündimpulse 
erzeugst.


Wie erzeugst du denn die Pulse?
Wenn das zb ein Timer im CTC Modus ist, dann hast du schon deinen 
Übeltäter. Dessen Top Wert (OCR1A) wird 'immediate' upgedated und nicht 
synchronisiert mit dem Erreichen des Top Wertes.

Und damit kann dir dann folgendes passieren:

Angenommen der Top Wert steht auf 200. Jetzt bestimmst du einen neuen 
Wert, der sei 100.
Wie es der Zufall so will, hat der Timer zum Zeitpunkt der Zuweisung an 
das OCR1A Register schon den Wert 150 erreicht. Was ja auch kein Problem 
wäre, denn bei 200 würde ja die Aktion ausgelöst und der Timer wieder 
auf 0 gesetzt.
Jetzt weißt du aber dem OCR1A einen neuen Wert von 100 zu. 150 ist 
größer als 100. Nur: das interessiert keinen. Der Compare Match kommt 
bei Gleichstand, und 150 ist nun mal kein Gleichstand mit 100. Auf der 
anderen Seite wird der Timer weiterzählen, 151, 152, 153, ... usw. Bei 
200 passiert nichts mehr, denn OCR1A ist ja nicht mehr 200 sondern 100. 
Also wird der Timer weiterzählen 201, 202, 203, ... bis er irgendwann 
dann auch mal die 65535 erreicht und es dann bei 0 weiter geht. Da zählt 
der Timer dann 0, 1, 2, 3, ... 98, 99, 100. Und erst jetzt ist der 
nächste Compare Match fällig, denn erst jetzt stimmen ja OCR1A und der 
Zählerstand überein.

Durch das Verringern des OCR1A Wertes von 200 auf 100 bist du also bei 
aktuellen Timer-Zählerständen zwischen 100 und 199 in der Situation, 
dass der Timer weder bei 200, noch bei 100 den Compare Match auslöst, 
sondern erst mal komplett 'rundum laufen' muss, ehe dann der Mechanismus 
erneut in Gang kommt.

Das Problem ist die nicht synchronisierte Zuweisung eines neuen OCR1A 
Wertes, der kleiner ist als der aktuelle Zählerstand des Timers zum 
Zeitpunkt der Zuweisung.

: Bearbeitet durch User
von Martin K. (maart)


Lesenswert?

Augenkrebs!
1
 ADMUX |= (0<<MUX0) | (0<<MUX1) | (0<<MUX2);

von Kaj (Gast)


Lesenswert?

Eine Null kann man nicht Shiften...
Erst eine Eins zur Zielposition shiften und dann negieren.

von Daniel (Gast)


Lesenswert?

Danke Karl-Heinz,

deine Erklärung ist sehr gut und nachvollziehbar!
Leider bin ich mir nicht sicher was du mit synchronisierter Zuweisung 
eines neuen Vergleichswerts meinst. Auf was müsste die Zuweisung denn 
synchronisiert werden?

Meine Zündimpulse erzeuge ich nach folgendem Schema:
1
ALPHA = ((ALPHAmax - ALPHAmin)/(1023)) * (ADC_Lesen()) + ALPHAmin;
2
3
ISR (INT0_vect)        
4
{
5
     TCNT1 = 0;
6
     OCR1A = ALPHA;
7
}
8
9
ISR (TIMER1_COMPA_vect)          
10
{
11
  if(gezuendet)
12
  {
13
    zweiteHalbwelle = 1;
14
    ausschaltsicherung = 0;
15
    weiterZuenden = 1;
16
17
    if(drehrichtung)
18
    {
19
      //Zündung andere Drehrichtung
20
    }
21
    else
22
    {
23
      PORTA |= (1<<PINA4);      
24
      PORTA |= (1<<PINA5);      
25
    }
26
27
    OCR1B = ALPHA + 400;        
28
  }
29
}
30
31
ISR (TIMER1_COMPB_vect)          
32
{
33
  if(gezuendet)
34
  {
35
    if(weiterZuenden)
36
    {
37
      ausschaltsicherung = 1;
38
39
        PORTA &= ~(1<<PINA4);    
40
        PORTA &= ~(1<<PINA5);    
41
        PORTA &= ~(1<<PINA6);    
42
        PORTA &= ~(1<<PINA7);    
43
44
      if(zweiteHalbwelle)
45
      {
46
        zweiteHalbwelle = 0;
47
        OCR1C = ALPHA + 20000;
48
      }
49
      else
50
      {
51
        weiterZuenden = 0;
52
      }
53
    }
54
  }
55
}
56
57
ISR (TIMER1_COMPC_vect)    
58
{
59
  if(gezuendet)
60
  {
61
    if(weiterZuenden)
62
    {
63
      ausschaltsicherung = 0;
64
65
      if(drehrichtung)
66
      {
67
        //Zündung andere Drehrichtung!!!
68
      }
69
      else
70
      {
71
        PORTA |= (1<<PINA6);  
72
      }
73
74
      OCR1B = ALPHA + 20400;
75
    }
76
  }
77
}
78
79
void Timer1Init(void)  
80
{
81
  TCCR1B = 0x00;          
82
  TCCR1A = 0x00;          
83
84
  TIMSK1 |= (1<<OCIE1A) | (1<<OCIE1B) | (1<<OCIE1C); 
85
86
  EIMSK |= (1<<INT0);        
87
  EICRA |= (1<<ISC01);       
88
89
  TCCR1B = 0x02;          
90
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Kannst du denn das ganze nicht so machen, dass die OCR Werte nicht fixe 
Zahlenwerte sind, sondern sich in Bezug auf den jeweils letzten OCR Wert 
errechnen und dafür der Timer dann einfach durch lauft?
Denn wenn ich etwas gelernt habe, dann das, das dieses Timer auf 0 
setzen mehr Probleme mit sich bringt als es löst.
1
ISR (TIMER1_COMPA_vect) 
2
{
3
  ....
4
5
6
  OCR1B = OCR1A + ALPHA + Fixer_Offset;
7
  OCR1C = OCR1A - 1;   // sicherheitshalber, damit der nicht im Weg ist
8
}
9
10
ISR (TIMER1_COMPB_vect)
11
{
12
  ....
13
14
  OCR1C = OCR1B + ALPHA + Fixer_Offset;
15
  OCR1A = OCR1B - 1;   // sicherheitshalber
16
}
17
18
ISR (TIMER1_COMPC_vect) 
19
{
20
  ...
21
22
  OCR1A = OCR1C + ALPHA + Fixer_Offset;
23
  OCR1B = OCR1C - 1;   // sicherheitshalber, damit OCR1B nicht unbeabsichtigt auslöst
24
}

so ungefähr. Jetzt kannst du ALPHA verändern wie du lustig bist, die 3 
Interrupts feuern nach wie vor reihum dahin und sobald ALPHA einen neuen 
Wert kriegt wird die zeitliche 'Distanz' zwischen den 3 Interrupts 
entsprechend verringert oder verkleinert.

Der Timer läuft durch und jede ISR rechnet aus, wenn die jeweils nächste 
ISR drann ist zu feuern und setzt dann den entsprechenden OCR Wert ins 
richtige Register (und sorgt dafür, dass dann auch wirklich die nächste 
ISR drann kommt, indem sie den OCR Wert für den jeweils 3-ten ISR AUfruf 
auf die maximal mögliche Zeit setzt. Man hätte statt dessen auch den 
jeweils nicht benötigten Interrupt sperren können, muss dann aber wieder 
auf die Interrupt Flags aufpassen).

Das einzige worauf du achten musst: Das der Ausdruck
1
     Alpha + Fixer_Offset
nicht größer als der Zählbereich des Timers wird. Bei 16 Bit also nicht 
größer als 65535.


Edit:
Mir fällt gerade auf, dass ich unter 'Lage der Zündpulse' eventuell 
etwas anderes verstehe als du. Ich deute die Verschiebung der Zündpulse 
als Mittel um die 'Drehzahl' der ABlauffolge der Zündpulse zu verändern.

: Bearbeitet durch User
von Jan (Gast)


Lesenswert?

Beschreibe doch am besten mal die Grundaufgabe, sprich Randbedingungen.
Was willst du genau tun bzw. wofür. Das würde schon einmal helfen, wie 
der Programmablauf auszusehen hat und was du eigentlich willst. Alles 
andere ist für mich im Moment noch etwas blindes Raten.

von Daniel (Gast)


Lesenswert?

Ich denke ich habe nicht deutlich genug gemacht was ich mit dem 
Verschieben der Zündimpulse bewirken will.
Realisiert wird damit eine Phasenanschnittsteuerung für eine 
Gleichstrommaschine. Das heißt mit den Zündimpulsen werden Thyristoren 
in den leitenden Zustand gebracht, die nach erfolgter Zündung die 
Netzspannung angeschnitten auf den Motor gibt. Die Lage der Zündimpulse 
wird dabei mit INT0 auf den Nulldurchgang der Netzspannung 
synchronisiert.
OCR1B ist nur dafür zuständig, die Breite der Zündimpulse festzulegen 
und muss daher einen festen Wert erhalten.
Die Reihenfolge der Vergleichswerte muss also immer die gleiche sein:

OCR1A(einschalten) -> OCR1B(ausschalten) -> OCR1C(einschalten) -> 
OCR1B(ausschalten).

von Ingo (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Angenommen der Top Wert steht auf 200. Jetzt bestimmst du einen neuen
> Wert, der sei 100.
> Wie es der Zufall so will, hat der Timer zum Zeitpunkt der Zuweisung an
> das OCR1A Register schon den Wert 150 erreicht. Was ja auch kein Problem
> wäre, denn bei 200 würde ja die Aktion ausgelöst und der Timer wieder
> auf 0 gesetzt.
> ...

Hier wurde alles gesagt, hier liegt der Fehler. Du darfst dein OCR1A nur 
dann mit ALPHA updaten, wenn das OCR1A = 0 ist, also im Timeroverflow 
oder bei einem Compare-Match. Ansonsten hast du diese überlangen Pulse, 
das ist normal!

von Karl H. (kbuchegg)


Lesenswert?

In dem Fall würde ich dann mal schätzen, dass sich bei bestimmten ALPHA 
Werten eine 'Überlappung' oder gar 'Vertauschung' der Reihenfolge der 
OCR Werte ergibt.

AUch erhebt sich die Frage, ob du während die ISR Sequenz abläuft, das 
ALPHA überhaupt verändern darfst. Im Zweifelsfall mal so
1
volatile uint16_t AlphaToUse;
2
3
ISR (INT0_vect)        
4
{
5
     TCNT1 = 0;
6
     AlphaToUse = ALPHA;
7
8
     OCR1A = AlphaToUse;
9
}
10
11
ISR( .... 
12
13
14
   mit AlphaToUse anstelle von ALPHA arbeiten

somit ist sichergestellt, dass der Alpha Wert während eines kompletten 
Zykluses konstant bleibt. Der nächste Zyklus kann dann wieder sein 
eigenes neues Alpha haben. Aber für diesen einen Zyklus ist es auf einen 
fixen Wert eingefroren.

von Daniel (Gast)


Lesenswert?

Vielen Dank Karl Heinz,

jetzt funktioniert alles so wie es soll!
Ich kann am Poti so schnell drehen wie es nur geht und meine Zündimpulse 
kommen immer sauber wie sie sollen.

Nochmals vielen Dank und schöne Grüße!

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.