Timer-Vergleichswerte beim Einschalten scheinbar zufällig

OP #3229264
Lesenswert?

Hallo,

ich programmiere eine Zündsteuerung in C auf dem ATMega 2561 für eine 
Thyristorschaltung.
Die Steuerung soll durch Drücken eines Tasters gestartet werden und 
danach per Timer-Interrupts netzsynchrone Zündimpulse ausgeben.
Das Ausgeben der Zündimpulse funktioniert einwandfrei, das heißt ich 
kann mir diese auf dem Oszilloskop anschauen und diese auch per 
Potentiometer verschieben.

Nun zu dem Problem:
Wenn ich nach dem Reset per Tastendruck die Steuerung einschalte, werden 
im ersten, sehr kurzen Moment, scheinbar zufällig, zu lange Impulse 
ausgegeben welche zu einem Kurzschluss führen. Nach diesem 
Einschaltvorgang sind aber alle Impulse sauber da und das Programm läuft 
einwandfrei. Beim erneuten Aus-/Einschalten tritt das Problem wieder 
auf.

Zusätzlich verwende ich noch einen zweiten Taster, welcher nur zwischen 
zwei Thyristor-Gruppen wechselt, allerdings keine Interrupts ein- oder 
ausschaltet. Hier ist zu beobachten, dass ein Druck auf diesen Taster 
keines der oben genannten Probleme verursacht, sondern sauber die 
jeweiligen Impulse ausgegeben werden.


Hier der gesamte Quellcode:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include "ButtonPress.h"
5

6
/*****************************VARIABLEN-Deklaration****************/
7
int einschaltzustand = 0;
8
int zweiteHalbwelle = 0;
9
int ausschaltsicherung = 0;
10
volatile int Drehrichtung = 0;
11
 
12

13
uint16_t ADC_Wert;
14

15
int ALPHA = 0;
16
int ALPHAmin = 6500;  
17
int ALPHAmax = 18000;   
18

19
/*****************************PORT-Initialisierung*****************/
20
void PortInit(void)
21
{
22
  DDRD &= ~(1<<PIND0);              
23
  DDRF &= ~(1<<PINF0);              
24
  DDRA &= ~((1<<PINA2) | (1<<PINA1));        
25
  PORTA |= (1<<PINA2) | (1<<PINA1);        
26
  
27
  DDRA |= (1<<PINA7) | (1<<PINA6) | (1<<PINA5) | (1<<PINA4);      
28
  PORTA &= ~((1<<PINA7) | (1<<PINA6) | (1<<PINA5) | (1<<PINA4));    
29
  DDRB |= (1<<PINB2) | (1<<PINB3) | (1<<PINB4);            
30
  PORTB |= (1<<PINB2) | (1<<PINB3) | (1<<PINB4);            
31
}
32

33
/*****************************TIMER1-Initialisierung***************/
34
void Timer1Init(void) 
35
{
36
  TCCR1B = 0x00;  
37
  TCCR1A = 0x00;  
38

39
  TIMSK1 |= (1<<OCIE1A) | (1<<OCIE1B) | (1<<OCIE1C); 
40

41
  EIMSK |= (1<<INT0);        
42
  EICRA |= (1<<ISC01);        
43

44
  TCCR1B = 0x02;          
45
}
46

47
void AdcInit(void)
48
{
49
   ADMUX |= (0<<MUX0) | (0<<MUX1) | (0<<MUX2);     
50
   ADMUX &= ~((1<<REFS0) | (1<<REFS1));       
51
   ADMUX|=(0<<ADLAR);                 
52
   ADCSRA |=(1<<ADSC);               
53
   ADCSRA |=(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);  
54
}
55

56
uint16_t ADC_Lesen(void)
57
{
58
  ADCSRA |=(1<<ADEN);           
59
  ADCSRA |= (1<<ADSC);              
60
  while (ADCSRA & (1<<ADSC));      
61

62
  ADCSRA &= ~(1<<ADEN);        
63
  return ADCW;               
64
}
65

66
/*****************************Initialisierung***************/
67
void Init(void)
68
{
69
  PortInit();
70
  Timer1Init();
71
  AdcInit();
72
  PORTB &= ~(1<<PINB3);  
73
}
74

75
/******************************EXTERNER Interrupt*****************/
76
ISR (INT0_vect)
77
{      
78
  OCR1A = ALPHA;
79
  TCNT1 = 0;
80
}
81

82
/******************************SOFTWARE Interrupts****************/
83
ISR (TIMER1_COMPA_vect)
84
{  
85
  if(Drehrichtung == 0)
86
  {
87
    PORTA |= (1<<PINA4);            
88
  }
89
  else
90
  {    
91
      //PORTA |= (1<<PINA7);    
92
  }
93
  OCR1B = ALPHA + 2000;  
94

95
  zweiteHalbwelle = 0;
96
  ausschaltsicherung = 0;
97
}
98

99
ISR (TIMER1_COMPB_vect)
100
{    
101
  if(Drehrichtung == 0)
102
  {
103
    PORTA &= ~(1<<PINA4);    
104
    PORTA &= ~(1<<PINA5);    
105
  }
106
  else
107
  {
108
    PORTA &= ~(1<<PINA6);    
109
    PORTA &= ~(1<<PINA7);    
110
  }
111

112
  ausschaltsicherung = 1;
113

114
  if(zweiteHalbwelle == 0)
115
  {
116
    OCR1C = ALPHA + 20000;
117

118
    zweiteHalbwelle = 1;
119
  }
120
}
121

122
ISR (TIMER1_COMPC_vect)  
123
{  
124
  if(Drehrichtung == 0)
125
  {
126
    PORTA |= (1<<PINA5);    
127
  }
128
  else
129
  {    
130
    //PORTA |= (1<<PINA6);    
131
  }
132

133
  OCR1B = ALPHA + 22000;    
134

135
  ausschaltsicherung = 0;
136
}
137

138
/****************************MAIN function*************************/
139
int main(void)
140
{
141
  cli();  
142
  Init();  
143

144
  while(1)
145
  {  
146
    ADC_Wert = ADC_Lesen();
147
    ALPHA = ((ALPHAmax - ALPHAmin)/(1024)) * (ADC_Wert) + ALPHAmin;
148

149
    if(ButtonPressed(0, PINA, 2, 500))  
150
    {
151
      if(einschaltzustand == 0)  
152
      {
153
        sei();
154

155
        PORTB |= (1<<PINB3);    
156

157
        if(Drehrichtung == 1)
158
        {
159
          PORTB &= ~(1<<PINB4);  
160
        }
161
        else
162
        {
163
          PORTB &= ~(1<<PINB2);            
164
        }
165

166
        einschaltzustand = 1;
167
      }
168
      else            
169
      {
170
        if(ausschaltsicherung == 0)
171
        {
172
          _delay_us(2500);
173
        }
174
        else
175
        {
176
          cli();
177

178
          PORTB |= (1<<PINB2);  
179
          PORTB |= (1<<PINB4);  
180
          PORTB &= ~(1<<PINB3);  
181

182
          einschaltzustand = 0;
183
        }
184
      }
185
    }
186

187
    if(einschaltzustand == 1)
188
    {
189
      if(ButtonPressed(1, PINA, 1, 500))  
190
      {
191
        if(Drehrichtung == 0)    
192
        {  
193
          PORTB &= ~(1<<PINB4);  
194
          PORTB |= (1<<PINB2);  
195
          Drehrichtung = 1;
196
        }
197
        else            
198
        {
199
          PORTB &= ~(1<<PINB2);  
200
          PORTB |= (1<<PINB4);  
201
          Drehrichtung = 0;
202
        }
203
      }
204
    }
205
  }
206
}

Hier der Quellcode für die "ButtonPress" Funktion, vielleicht hängt es 
auch damit zusammen:
1
#include<avr/io.h>
2

3

4

5
DDRC |= 1 << PINC5; 
6
PORTC ^= 1 << PINC5;
7
DDRC &= ~(1 << PINC4);
8
PORTC |= 1 << PINC4;
9

10
int Pressed = 0;
11
int Pressed_Confidence_Level = 0; 
12
int Released_Confidence_Level = 0; 
13

14

15
if (bit_is_clear(PINC, 4))
16
{
17
  Pressed_Confidence_Level ++; 
18
  Released_Confidence_Level = 0; 
19
  if (Pressed_Confidence_Level >500) 
20
  {
21
    if (Pressed == 0)
22
    {
23
      PORTC ^= 1 << PINB5;
24
      Pressed = 1;
25
    }
26
    Pressed_Confidence_Level = 0;
27
  }
28
}
29
else
30
{
31
  Released_Confidence_Level ++; 
32
  Pressed_Confidence_Level = 0; 
33
  if (Released_Confidence_Level >500)
34
  {
35
    Pressed = 0;
36
    Released_Confidence_Level = 0;
37
  }
38
}


Wenn mir jemand weiterhelfen kann wäre ich sehr dankbar, da ich mich 
schon seit 2 Tagen mit diesem Problem beschäftige und mir langsam die 
Lösungsansätze ausgehen...

Vielen Dank!
OP #3233490
Lesenswert?

Hallo Hubert,

ich habe mit diversen Widerstände (von 10k bis 330M) die Ausgänge 
beschaltet. Leider tritt das Problem sogar noch beim 330M unverändert 
auf.
Vielleicht hab ich das Problem auch nicht präzise genug erläutert. 
Deshalb füge ich 2 Screenshots vom Oszilloskop in den Anhang.
Der Erste zeigt genau das Problem, wobei erwähnt werden muss, dass 
dieser nur einer von vielen Verläufen ist und sich selten wiederholt. 
Dies tritt fast jedesmal auf, wenn man mit dem Taster die Interrupts mit 
dem SEI-Befehl global zulässt.
Auf dem zweiten Screenshot ist der Verlauf einen kurzen Moment nach dem 
Tastdruck dargestellt und ist absolut zufriedenstellend.
#3233505
Lesenswert?

Daniel Kalbreyer schrieb:
> wenn man mit dem Taster die Interrupts mit
> dem SEI-Befehl global zulässt.

Aha.
Wenn Du erst per Taste Interrupts freigibst, drängeln sich natürlich 
alle bis dahin aufgelaufenen Interrupts und werden schnellstmöglichst 
behandelt.

Die (Nicht-)Freigabe behindert nicht das Setzen der Pending-Flags.
Das wäre ja auch äußerst blöd, dann könnten Interrupts verloren gehen.
#3233527
Lesenswert?

Hmm.
So wie ich das sehe, genügt es nicht einfach nur mittels sei()/cli() die 
Interrupts zuzulassen. Denn: Der Timer läuft ja weiter. Und wenn dann 
der sei() gegeben wird, hast du keine Kontrolle darüber welcher der 
Comnpare-Match Interrupts als erster kommt, weil du ja keine Kontrolle 
über den Zählerstand des Timers hast.

D.h. du musst dir Logik einbauen, die sicherstellt, dass nach dem sei() 
erst mit einem Compare-Match A (wenn ich das richtig gesehen) die ganze 
Sache wieder ins Rollen kommt. Nachdem du mittels cli() die ganze Sache 
abgestellt hast, werden Compare Matches auf B bzw. C ignoriert und kein 
Ausgang geschaltet, solange bis der darauffolgende Match auf A alles 
wieder freigibt.

Auch bin ich mir nicht sicher, ob du einfach so mittels cli() die ganze 
Steuerung abdrehen darfst. Denn im schlimmsten Fall kommt der cli() 
genau mitten im Puls. Wodurch dir dann der Ausgang im letzten Zustand 
hängen bleibt.


-> Ich würde das ganze NICHT mittels sei()/cli() lösen, sondern mit 
einer globalen Variablen, die aussagt, ob die Zündung eingeschaltet ist 
oder nicht. Im Compare Match A wird diese Variable abgefragt und nur 
wenn freigegeben, wird auch eine weitere Variable auf true gesetzt, die 
im Compare Match B bzw. C abgefragt wird und nur dann, wenn die auf true 
steht, dann werden auch die Aktionen gemacht. Im Compare Match C wird 
die Variable dann wieder auf false gesetzt, sodass die nächste 
Pulssequenz durch einen erneuten COmpare Match A erst wieder freigegeben 
werden muss. Kommt diese Freigabe nie (weil du sie per Taster abgedreht 
hast), dann erfolgen auch die Aktionen im Match B bzw. C nicht.

1
volatile uint8_t doSparkle;
2
volatile uint8_t continueSparkle;
3

4
ISR (TIMER1_COMPA_vect)
5
{
6
  if (doSparkle)
7
  {
8
    continueSparkle = TRUE;
9

10

11
    if(Drehrichtung == 0)
12
    {
13
      PORTA |= (1<<PINA4);            
14
    }
15
    else
16
    {    
17
    .....
18
  }
19

20
  OCR1B = ALPHA + 2000;
21
  ....
22
}
23

24
ISR (TIMER1_COMPB_vect)
25
{
26
  if (continueSparkle)
27
  {
28
    if(Drehrichtung == 0)
29
    {
30
    ....
31
  }
32
}
33

34
ISR (TIMER1_COMPC_vect)  
35
{  
36
  if (continueSparkle)
37
  {
38
    continueSparkle = FALSE;
39

40
    if(Drehrichtung == 0)
41
      ....
42
  }
43
}
44

45

46
int main()
47
{
48
  ....
49

50

51
     if( irgendwas Taster gedrückt )
52
       doSparkle = TRUE;
53

54
     if( irgendein anderer Taster
55
       doSparkle = FALSE;
56

57

58
}

der Timer läuft durch und auch die Interrupts sind zu allen Zeiten 
aktiv.
Auf die Art ist sichergestellt, dass
* die komplette an den Pins sichtbare Sequenz immer mit einem Compare 
Match A beginnt
* auch nach abschalten der Zündung die komplette Pin-Sequenz bis zu 
einem Compare Match C durchlaufen wird.

D.h. im Grunde: Die Logik der Zündung läuft die ganze Zeit. Lediglich 
das Schalten der Ausgangspins wird durch Variablen unterdrückt, der 
ganze OCR Mechanismus läuft weiter. Wodurch extern gesehen, die Zündung 
abgeschaltet ist. Aber: sie läuft sauber an und sie stoppt auch sauber.
OP #3233648
Lesenswert?

Vielen Dank Karl-Heinz,

die Idee ist brillant und funktioniert mit ein paar kleinen Änderungen 
einwandfrei.
Die Zündimpulse kommen sauber beim Einschalten und machen keinen Ärger 
mehr.

Du hast mir sehr weiter geholfen und mich vor einem weiteren 
deprimierenden Abend bewahrt.

Nochmals vielen Dank und viele Grüße,
Daniel

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