Probleme bei Programmausführung

OP #2564485
Lesenswert?

Hallo,

mein ATmega16 führt das draugeflashte Programm zunächst richtig aus.

Dann stoppt er. Macht Quatsch.

Danach geht es dann wieder Ordnungsgemäß.

Dann stoppt er. Macht Quatsch.

Danach geht es dann wieder Ordnungsgemäß.

Dann stoppt er. Macht Quatsch.

[...]

Bis er schließlich ganz aufgibt.


Es sind auch keine festen Zeitabstände dazwischen.


Ordnungsgemäß heisst: PORTC verschieden setzen. (Ausgang)


Entw-Board: easyAV6. Die ADC´s sind in Betrieb, ein LCD an PORT B und 
eine PWM an PD5.

Ich hab den uC auch schon ausgetauscht.

Da der Code länger ist wollte ich Ihn hier nicht direkt posten.
OP #2564498
Lesenswert?

1
 
2
#define TASTERPORT PIND
3
#define TASTERBIT1 PD2
4
#define TASTERBIT2 PD3
5

6
#include <avr/io.h>
7
#include <stdlib.h>
8
#include <util/delay.h>
9
#include "lcd-routines.h"
10
#include "lcd_functions.c"
11
#include <avr/interrupt.h>
12
#include "entprellung.h"
13

14

15
volatile int gew_Diode = 4;
16
volatile int adcval_max = 0;
17
volatile int adcval_alt = 0;
18
volatile int adcval;  //für das ADC-Ergebnis
19
volatile int adcval_neu;
20
volatile int an= 0x11;      //linkslauf
21
volatile int andersrum = 0x22;  //rechtslauf
22

23
volatile int zaehler=0; //für ADC-ISR
24

25
volatile int differenz;
26
volatile int differenz_alt  = 0;
27
volatile int differenz_alt2 = 0;
28

29
volatile double zeit;
30
volatile double altezeit;
31

32
char Buffer2[20]; //für das ADC-Ergebnis
33
char Buffer3[20]; //für die gewählte Diode
34

35
//-----------------------------------------
36
// ADC-Funktionen
37
//-----------------------------------------
38

39
/* ADC initialisieren */
40
void ADC_Init(void) 
41
{
42
 
43
    uint16_t result;
44
 
45
    ADMUX = (0<<REFS1) | (0<<REFS0);    // PIN AREF als Referenz nutzen
46
    ADCSRA = (1<<ADPS1) | (1<<ADPS0);   // Frequenzvorteiler : 8  
47
    ADCSRA |= (1<<ADEN);                // ADC aktivieren
48
  ADCSRA |= (1<<ADIE);        // ADC-Interrupt aktivieren
49
 
50
    /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
51
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
52
 
53
    ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
54
    while (ADCSRA & (1<<ADSC) );          // auf Abschluss der Konvertierung warten
55
    /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der naechsten
56
     Wandlung nicht uebernommen. */
57
    result = ADCW;
58
}
59
 
60
/* ADC Einzelmessung */
61
uint16_t ADC_Read( uint8_t channel )
62
{
63
    // Kanal waehlen, ohne andere Bits zu beeinflu?en
64
    ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
65
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
66
    while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
67
    ;
68
    return ADCW;                    // ADC auslesen und zurueckgeben
69
}
70
 
71
/* ADC Mehrfachmessung mit Mittelwertbbildung */
72
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t average )
73
{
74
    uint32_t result = 0;
75
 
76
    for (uint8_t i = 0; i < average; ++i )
77
  {
78
      result += ADC_Read( channel );
79
    _delay_ms(20);
80
   }
81
    return (uint16_t)( result / average );
82
}
83

84
//-----------------------------------------
85
// Motor-Drehrichtung
86
//-----------------------------------------
87

88
void change_direction()
89
{
90
  if( (an & 0x11) == 0x11)
91
  {
92
    an = 0x02;
93
    andersrum = 0x11;
94
  }
95
  else if ( (an & 0x22) == 0x02)
96
  {
97
    an = 0x01;
98
    andersrum = 0x22;
99
  }
100
}
101

102
//-----------------------------------------
103
//Interrupt Service Routinen
104
//-----------------------------------------
105

106

107
ISR(INT0_vect)  //PD2 gedrückt
108
{
109
  
110
  int pgd = tasterhoch();
111

112
    if( (pgd == 1 ) ^ (gew_Diode < 8) )
113
    {
114

115
      if( gew_Diode == 2)
116
      {
117
        gew_Diode +=2;
118
        
119
      }
120
  
121
      else if ( gew_Diode == 5)
122
            {
123
                 gew_Diode +=2;           
124
      }
125

126
      else
127
      {
128
        gew_Diode +=1;
129
      }
130
            
131
    }
132

133
    else
134
    {
135
      
136
    }
137
  
138
    an = 0x22;      //rechtslauf
139
  andersrum = 0x11;  //linkslauf
140

141
  adcval_max = 0;
142
  zaehler = 0;
143

144
  //Erste Zeile löschen
145
  lcd_setcursor( 0, 1 );
146
  lcd_string("                 ");
147
    lcd_setcursor( 0, 1 );
148
    
149
  //Ausgabe der gewählten Diode auf dem LCD
150
  itoa( gew_Diode, Buffer3, 10 );
151
    lcd_string("Diode ");
152
  lcd_string(Buffer3);
153

154
}
155

156
ISR(INT1_vect)  //PD3 gedrückt
157
{
158
  int mgd = tastertief();
159
         
160
    if ( (mgd == 1) ^ (gew_Diode > 1) )
161
    {
162

163
      if( gew_Diode == 2)
164
      {
165
        gew_Diode = gew_Diode;        
166
      }
167

168
      else if ( gew_Diode == 4)
169
            {
170
                 gew_Diode -=2;
171
      }
172

173
      else if ( gew_Diode == 7)
174
            {
175
                 gew_Diode -=2;
176
      }
177

178
      else
179
      {
180
        gew_Diode -=1;
181
      }
182

183
    }
184

185
    else
186
    {
187
      
188
    }
189

190
  an = 0x11;      //linkslauf
191
  andersrum = 0x22;  //rechtslauf
192

193
  adcval_max = 0;
194
  zaehler = 0;
195

196
  //Erste Zeile löschen
197
  lcd_setcursor( 0, 1 );
198
  lcd_string("                 ");
199
  lcd_setcursor( 0, 1 );
200
    
201
  //Ausgabe der gewählten Diode auf dem LCD
202
  itoa( gew_Diode, Buffer3, 10 );
203
  lcd_string("Diode ");
204
  lcd_string(Buffer3);
205

206
}
207

208

209
ISR(ADC_vect)  //Interrupt nach Abschluss einer ADC Wandlung
210
{
211

212
  if(adcval > 0)
213
  {
214
    adcval_neu = ADC_Read_Avg( (gew_Diode - 1),100); //neuer ADC_Wert zum "Arbeiten"
215

216
    if(adcval_neu > adcval_max)
217
    {  
218
      adcval_max = adcval_neu;  
219
    }
220

221
    if(adcval_neu < adcval_alt)
222
      
223
    {
224
      change_direction();
225
    }
226

227
    /*
228

229
    differenz = 300 - adcval_neu;                        //300 = Sollwert
230
    //                q0              q1                  q2
231
    zeit =  0.1*differenz + 0.2*differenz_alt + 0.3*differenz_alt2;  //Reglergleichung
232

233
    differenz_alt2 = differenz_alt;
234
    differenz_alt = differenz;
235
    altezeit = zeit;
236

237
    int zeitganz = (int) zeit;
238

239
    lcd_setcursor( 10, 1 );
240
    lcd_int(zeitganz);
241

242
    if(zeit>80)
243
    {
244
      zeit = 80;
245
    }
246

247
    PORTC = an;
248
    _delay_ms(zeitganz);
249
    PORTC = andersrum;
250
    _delay_ms(zeitganz/2);
251

252
    */
253

254
    zaehler += 1;
255
    
256
    if(zaehler == 1)
257
    {  
258
      PORTC = an; 
259
      _delay_ms(180);
260
      PORTC = andersrum;
261
      _delay_ms(50);
262
      
263
      PORTC = 0x00;  //STOPP - Laserpointerstrahl ist prinzipiell auf der Diode
264
    }
265

266
    else if(( (zaehler >=31) & ((adcval_max - adcval_neu) < 50) ))
267
    {
268
      PORTC = 0x00;
269
    }
270

271
    else  // 1 < zaehler < 31
272
    {
273
      
274
      PORTC = an;
275
      _delay_ms(80);
276
      PORTC = andersrum;
277
      _delay_ms(50);
278
      PORTC = 0x00;
279
      
280
      
281
      
282
    }
283
  }
284
  
285
  else
286
  {
287
    PORTC = an;
288
    _delay_ms(100);
289

290
    PORTC = andersrum;
291
    _delay_ms(50);
292

293
    PORTC = 0x00;
294

295
    zaehler = 0;
296
  }
297

298
  adcval_alt = adcval_neu;
299

300
}
301

302
//-----------------------------------------
303
// Start Hauptprogramm
304
//-----------------------------------------
305
 
306
int main(void)
307
{
308

309
//-----------------------------------------
310
// Datenrichtung (Ein-/Ausgänge) setzen
311
//-----------------------------------------
312

313
  DDRC |= (1 << PD0 | 1 << PD1 | 1 << PD4 | 1 << PD5);
314

315
  DDRD |= (1 << PD4 | 1 << PD5);
316

317
//-----------------------------------------
318
// PWM-Einstellungen
319
//-----------------------------------------  
320

321
  //            COM: verhalten bei match                               WGM: Phase Correct Mode  
322
  TCCR1A |= ( 1 <<COM1A1 | 0 << COM1A0| 1 << COM1B1| 0 << COM1B0| 1 << WGM11 | 0 << WGM10 );
323
  //            WGM: Phase Correct Mode              CS Prescaler
324
  TCCR1B |= ( 1 << WGM13 | 0 << WGM12 | 0 << CS12 | 0 << CS11 | 1 << CS10);
325

326
  
327
  ICR1 = 8000;  // Frequenz 500 Hz mit prescaler 1
328

329
  OCR1A = 2000;
330

331
//-----------------------------------------
332
// ADC einstellen / aktivieren
333
//-----------------------------------------  
334

335
  ADC_Init();
336

337
//-----------------------------------------
338
// Interrupt-Register
339
//-----------------------------------------  
340

341
  MCUCR = 0x0F;  //auf steigende Flanken reagieren (Taster mit externem Pull-Down)
342

343
  GICR = 0xC0;  //Interrupts INT0 + INT1 aktivieren
344

345
//-----------------------------------------
346
// für die Ausgabe auf dem LCD
347
//-----------------------------------------  
348

349
  lcd_init();          // Initialisierung des LCD
350

351
  lcd_string("  Neustart");
352
    lcd_setcursor( 0, 2 );    // Die Ausgabemarke in die 2te Zeile setzen
353
    lcd_string("    Neustart");
354

355
    _delay_ms(1000);
356
  
357
  //Erste Zeile löschen
358
  lcd_setcursor( 0, 1 );
359
  lcd_string("                 ");
360
  lcd_setcursor( 0, 1 );
361
    
362
  //Ausgabe der gewählten Diode auf dem LCD
363
  itoa( gew_Diode, Buffer3, 10 );
364
  lcd_string("Diode ");
365
  lcd_string(Buffer3);  
366
  
367
  sei(); //Interrupts anschalten
368

369
//-----------------------------------------
370
// Start Endlosschleife
371
//-----------------------------------------
372

373
    while(1) 
374
  {  
375
    if( ((PORTC == 0x00) & (zaehler >=31)) )
376
    {
377
      //2. Zeile löschen
378
        lcd_setcursor( 0, 2 );
379
      lcd_string("                 ");
380
        lcd_setcursor( 0, 2 );      
381

382
      lcd_string("Ausgerichtet :-)");
383
    }
384
    
385
    else
386
    {
387
      // Kanal x (gew_Diode - 1) wandeln
388
      adcval = ADC_Read(gew_Diode - 1);
389
      
390
      lcd_setcursor( 10, 1 );
391
      lcd_int(zaehler);
392

393
      //Umwandlung "Zahl" in einen "String"
394
      itoa( adcval_neu, Buffer2, 10 );
395

396
      //2. Zeile löschen
397
        lcd_setcursor( 0, 2 );
398
      lcd_string("                 ");
399
        lcd_setcursor( 0, 2 );  
400

401
      //Vorbereitung Ausgabe des Wandlungsergebnisses der gew. Diode
402
      lcd_string("ADC_Diode");
403
      lcd_string( Buffer3 );
404
      lcd_string(": ");
405
    
406
      //Ausgabe Wandlungsergebnis    
407
      lcd_string( Buffer2 );
408
    }
409
    
410
    _delay_ms(500);    
411

412
    }
413
 
414
    return (0);
415
}
Gast #2564514
Lesenswert?

du machst zuviel im der ISR. das LCD sollte an dieser stelle nicht 
beschrieben werden weil das viel zu lange dauert.

Das sollte aber nicht zu solchen Problemen führen.

Warum hast du immer int verwendet? Das ganze ist damit langsam und 
eventuell fehlerhaft weil alles operrationen mit der Variable mit atomic 
sind.
OP #2564519
Lesenswert?

Ordnungsgemäß:

PC0 setzen 0,1s halten
PC0 rücksetzen PC1 setzen 0,05s halten
PORTC ganz aus.

0,5sek Pause

PC0 setzen 0,1s halten
PC0 rücksetzen PC1 setzen 0,05s halten
PORTC ganz aus.

0,5sek Pause

[...]

Quatsch:

PORTC ganz aus.
PC0 setzen. (halten <  0,1s)
PC0 rücksetzen PC1 setzen  halten < 0,1s
PORTC ganz aus.

kurze Pause (länger als 0,5s)

dies 3-4 mal

nahcdem PORTC ganz aus ist
PC0 setzen. (extrem kurz)
PC0 rücksetzen PC1 setzen  (extrem kurz)
PORTC ganz aus.

danach geht es dann aber wieder ordnungsgemäß
#2564520
Lesenswert?

Aeh. Ja. Fehler zu haben ist ueblich. Dann ist nun Debuggen angesagt. 
Debuggen ist keine Krankheit, sondern Teil des Entwicklungsprozesses. 
Der Programmaufbau sollte auch schon mit Debuggen geplant sein.
Das bedeutet mit den kleinstmoeglichen Codeeinheiten gepruefte Bloecke 
aufzubauen. Dann lass das Ganze mal ohne Interrupts laufen. Dann mit 
einem, dann mit zweien. Bis der/die Fehler gefunden sind.
#2564526
Lesenswert?

Verrätst du uns auch noch, was das Ganze sein soll?

(In deinem Code findet sich ziemlich viel unübersichtliches und ich 
würde sogar nach einem ersten Drüberschauen sagen "unsinniges", dass es 
schwer ist, den Sinn hinter der ganzen Sache zu verstehen. Deine 
exzessive Verwendung von Hex-Konstanten macht die Sache dann auch nicht 
leichter.)
OP #2564548
Lesenswert?

Bei PORTA werden Signale von Fotodioden digitalisiert.

An PORTB hängt ein Display zur Ausgabe auf welche Diode sich ein 
Laserpointer ausrichten soll. Und zur Ausgabe des ADC-Wertes.

PORTC ist für die Steuerung einer H-Brücke. Links- / Rechtslauf eines 
Motors.

Auf PORTD(PD5) liegt eine PWM. (H-Brücke)

In der ADC-ISR soll gesteuert werden. (wenn möglich später geregelt).
#2564596
Lesenswert?

Geh bei der Fehlersuche systematisch vor.
Behebe zuerst die Fehler, die aus dem Quelltext offensichtlich sind und 
wir dir schon genannt haben.
Dann teste neu.

Fang z.B. damit an, die LCD-Ausgabe in Ordnung zu bringen. Vielleicht 
läuft das Programm ja korrekt, nur die Ausgabe spinnt?

Also: entweder alle LCD-Zugriffe aus den ISRs entfernen (gut), oder 
wenigstens während allen LCD-Zugriffen im main() die Interrupts sperren 
(quick&dirty).

(
schnelle Lösung: das _delay_ms(500) im Main durch
sei(); _delay_ms(500); cli(); ersetzen, das andere sei() weiter oben 
entfernen.
)

Dann schauen wir weiter.
OP #2564636
Lesenswert?

Brown-out kann ausgeschlossen werden.

Ich hab mittlerweile herausgefunden das er mit dem ADC_interrupt so 
seine Schwierigkeiten hat.
1
int main()
2
[...]
3
sei();
4

5
while(1) 
6
  {  
7
    //1. Zeile löschen
8
      lcd_setcursor( 0, 1 );
9
    lcd_string("                 ");
10
      lcd_setcursor( 0, 1 );
11
  
12
    //Ausgabe der gewählten Diode auf dem LCD
13
    itoa( gew_Diode, Buffer3, 10 );
14
      lcd_string("Diode ");
15
    lcd_string(Buffer3);
16

17
    if( ((PORTC == 0x00) & (zaehler >=31)) )
18
    {
19
      //2. Zeile löschen
20
        lcd_setcursor( 0, 2 );
21
      lcd_string("                 ");
22
        lcd_setcursor( 0, 2 );      
23

24
      lcd_string("Ausgerichtet :-)");
25
    }
26
    
27
    else
28
    {
29
      // Kanal x (gew_Diode - 1) wandeln
30
      //ADC_Read(gew_Diode - 1);
31
      
32
      lcd_setcursor( 10, 1 );
33
      lcd_int(zaehler);
34

35
      //Umwandlung "Zahl" in einen "String"
36
      itoa( adcval_neu, Buffer2, 10 );
37

38
      //2. Zeile löschen
39
        lcd_setcursor( 0, 2 );
40
      lcd_string("                 ");
41
        lcd_setcursor( 0, 2 );  
42

43
      //Vorbereitung Ausgabe des Wandlungsergebnisses der gew. Diode
44
      lcd_string("ADC_Diode");
45
      lcd_string( Buffer3 );
46
      lcd_string(": ");
47
    
48
      //Ausgabe Wandlungsergebnis    
49
      lcd_string( Buffer2 );
50
    }
51

52
    PORTC = an;
53
    _delay_ms(50);
54
    PORTC = andersrum;
55
    _delay_ms(50);
56
    PORTC = 0x00;
57
    
58
    _delay_ms(500);    
59

60
    }
61
 
62
    return (0);

Obwohl erst Interrupts vor der Endlosschliefe freigegeben werden. Und er 
auch eigentlich keinen werfen dürfte (ADC-Read ist auskommentiert). 
Springt er wenn er wenn im ADC-Register der ADC-Interrupt aktiviert wird 
in die ISR.

Die LCD-Zugriffe sind auch aus allen ISR raus.
#2564715
Lesenswert?

Marius S. schrieb:
> Obwohl erst Interrupts vor der Endlosschliefe freigegeben werden. Und er
> auch eigentlich keinen werfen dürfte (ADC-Read ist auskommentiert).
> Springt er wenn er wenn im ADC-Register der ADC-Interrupt aktiviert wird
> in die ISR.

Deine "ADC_init" schaltet zuerst den ADC Irq frei, und macht danach 
einen Dummy-Read. Dieser Dummy-Read löst natürlich auch einen IRQ aus.

Also:
 ADCSRA |= (1<<ADIE);
ans Funktionsende schieben.

Bei Global deaktivierten IRQs sollte das jedoch wurscht sein. Sicher 
dass der noch vor dem "sei()" dort reinspringt?
Eventuell enthält dein rest-Quelltext (lcd, enprellung, ..) ein sei() ?
Gast #2564764
Lesenswert?

Dein Programm ist nicht gerade "klein" (deine LCD-Routinen und 
entprellung kommen ja auch noch dazu) und du verwendest viele sehr große 
Variablen.. Gib mal folgendes auf deinem PC ein:
1
avr-size --mcu=atmega16 -C foo.elf
foo.elf ist die elf-Datei deines Programms, die wird beim Übersetzen in 
die .hex mit erzeugt.

Jetzt wird dir angzeigt wie viel RAM du schon verbraucht hast, 
vielleicht isses ja wirklich schon so weit dass du nen größeren 
Controller brauchst (Atmega32)
OP #2565706
Lesenswert?

Εrnst B✶ schrieb:
> -schumi- schrieb:
>> Jetzt wird dir angzeigt wie viel RAM du schon verbraucht hast,
>> vielleicht isses ja wirklich schon so weit dass du nen größeren
>> Controller brauchst (Atmega32)
>
> Soweit isses noch nicht... Mit wenig Aufwand könnte er z.B. alle
> String-Konstanten vom Ram in den Flash verbannen... PSTR(), lcd_string_P
> ()...

AVR Memory Usage
----------------
Device: atmega16

Program:    5670 bytes (34.6% Full)
(.text + .data + .bootloader)

Data:        426 bytes (41.6% Full)
(.data + .bss + .noinit)


Build succeeded with 0 Warnings...
OP #2565853
Lesenswert?

Mittlerweile läuft es recht stabil. Nach einer langen Zeit hat er immer 
noch nen kurzen Aussetzer.

-Ich mach jetzt direkt am Anfang der main erstmal ein cli();

-sei(); direkt vor der Endlosschleife.

-Ich hab mich um die Datenrichtung bei allen Ports gekümmert.

-Alle Interrupts schalte ich jetzt in der main, direkt vor dem sei(); ,
ein (externe (Taster), ADC).

-in der ADC_ISR:

   zuerst adif & adie = 0;

   am ende adie = 1

-Displayzugriffe alle ausserhalb der ISRn.

-Es läuft allerdings immer noch mit delays anstatt dem timer.


Und auch wenn es nicht zum Beitrag passt:

Habe ich ne Chance auf einen Regler wenn ich in dem Sinne keinen 
Sollwert habe außer das der entspr. Wert maximal sein soll?

Ich meine nein. Da ich ja in dem Sinne keinen Wert habe mit dem ich den 
aktuellen Wert vergleichen kann. Zudem habe ich ja dann nie eine 
Abweichung wo dann der Stellwert/Stellgröße entspr. 
zurückgefahren/invertiert wird.

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