Forum: Mikrocontroller und Digitale Elektronik Timer-Vergleichswerte beim Einschalten scheinbar zufällig


von Daniel K. (kallekalbreyer)


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!

von Hubert G. (hubertg)


Lesenswert?

Die Ausgänge sind von Moment des Einschalten der Spannung bis zur 
Initialisierung undefiniert hochohmig. Das könnte das von dir 
beschriebene Verhalten erklären.
Die Ausgänge über hochohmige Widerstände auf ein definiertes Potential 
legen.

von Daniel K. (kallekalbreyer)


Lesenswert?

Danke Hubert!

Leider kann ich das erst am Montag ausprobieren...
Ich meld mich wenn ichs ausprobiert habe.

Viele Grüße,
Daniel

von Daniel K. (kallekalbreyer)



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.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Daniel Kalbreyer schrieb:
> ich habe mit diversen Widerstände (von 10k bis 330M) die Ausgänge
> beschaltet

Und wie?

Daniel Kalbreyer schrieb:
> Dies tritt fast jedesmal auf, wenn man mit dem Taster die Interrupts mit
> dem SEI-Befehl global zulässt.

Lösch doch Vorher mal alle Interruptflags.

von Peter D. (peda)


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.

von Karl H. (kbuchegg)


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.

von Daniel K. (kallekalbreyer)


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

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.