Forum: Mikrocontroller und Digitale Elektronik globale Variable wird nicht in ISR genutzt (ATMega328P) WinAVR


von Avr N. (balze)


Lesenswert?

Hallo zusammen,

ich habe ein Problem und sehe den Wald vor lauter Baeumen nicht.

Ich habe eine ISR, die abhaengig vom eingelesenen ADC Wert einen Wert 
aus einer Tabelle (1024 Eintraege) an einen Timer weitergibt.

Es ist eigentlich so einfach, aber ich habe ein Problem, dass ich nicht 
erkennen kann.

Die Variable "mittelwert" scheint jedesmal vor dem Aufruf der ISR auf 0 
initialisiert zu werden.

Ich bekomme immer nur den Tabellenwert von ADC/2.

Ich habe testweise mal den "mittelwert" nur inkrementiert (in der ISR: 
mittelwert++; ).

Ich erhalte immer der Tabellenwert von 1 (oder 0, das kann ich nicht 
genau sagen, jedenfalls waechste "mittelwert" nicht an.

inkrementiere ich "mittelwert" in der while(1) der main (und benutze ihn 
in der ISR um den Timer zu beschreiben) wird definitiv inkrementiert.

Kann mir jemand sagen was ich hier falsch mache bzw. uebersehe ?

Vielen Dank im Voraus, mfG

Balze aka AVR Noob
1
#include <avr/io.h>
2
#include <avr/interrupt.h> 
3
#include <avr/pgmspace.h>
4
5
/*! \def arraysize 
6
 *  \brief Begrenzung der Feldgroesse\n
7
 *   Hier wird die Anzahl der Punkte pro Kurve festgelegt
8
*/
9
10
#define arraysize 1024          // Begrenzung der Feldgröße
11
12
#include "tabelle.h"            // Kennlinientabelle(n)
13
14
15
/*! \brief Interrupt Service Routine fuer das Setzen der Werte fuer Strom und Spannung\n\n
16
    Diese Routine wird aufgerufen, wenn der ADC seine Wandlung beendet hat. Dieser Wert wird\n
17
    in die Variable 'temp' umgespeichert und dann einer Begrenzung unterzogen, damit keine\n
18
    ungueltigen Feldwerte auftreten koennen. Dann wird ueber eine switch-Anweisung die entsprechende\n
19
    Kurve ausgewaehlt. Jetzt findet eine Pruefung statt, ob der Kurzschlusstrom erreicht wurde oder\n
20
    nicht. Wenn ja, dann werden feste Werte fuer Strom und Spannung vorgegeben und das Netzteil\n
21
    uebernimmt dann die Regelung. Falls der Kurzschlusstrom nicht erreicht wird, dann fungiert der\n
22
    Wert des Stromes als Index, worauf der enstprechende Wert aus der Tabelle genommen und\n
23
    und als PWM-Wert gesetzt wird. Der Wert fuer die Strombegrenzung wird so gesetzt, dass sie\n
24
    nicht eingreift.
25
26
*/
27
28
volatile uint16_t mittelwert;
29
30
ISR(ADC_vect) {
31
uint16_t SPG_Wert;
32
33
  SPG_Wert = ADCW;
34
35
    if (SPG_Wert>=arraysize) {
36
      SPG_Wert = arraysize-1;
37
  }
38
39
  mittelwert += SPG_Wert;      // Filterung der Eingangswerte
40
  mittelwert /= 2;
41
    
42
    OCR0A = 255;                        // Strombegrenzungswert
43
    
44
    OCR1A = pgm_read_word( &table1[mittelwert] );      // Kennlinienwert am gefilterten Eingangswert
45
}
46
47
48
/*! \brief Interrupt Service Routine fuer das Setzen des Abtastintervalls und Starten des ADC\n\n
49
    Mit dieser Routine wird das Abtastintervall festgelegt. Timer 2 (8-Bit) wurde so konfiguriert,\n
50
    das er im Fast-PWM-Modus laeuft und sobald er sein Maximum (0xFF) erreicht, diesen Interrupt\n
51
    ausloest. Mit einer Systemfreqenz von 20Mhz, eimem Vorteiler von 32 und seiner Zaehlweite von 0xFF\n
52
    laeuft dieser Interrupt mit rund 2450Hz. Sobald dieser aufgerufen wird, beginnnt der ADC mit der\n
53
    Umwandlung des analogen Wertes. Es ist zu beachten, dass dem ADC genuegend Zeit eingeraeumt wird,\n
54
    damit er die Umwandlung korrekt ausfuehren kann.
55
56
*/
57
58
/*
59
ISR(TIMER2_OVF_vect) {
60
  ADCSRA |= (1<<ADSC);                                           //Starte Umwandlung des analogen Wertes
61
}
62
*/
63
64
65
/*! \fn main
66
 *  \brief Hauptprogramm\n
67
 *   Hier stehen die Einstellungen fuer die\n
68
 *   Timer und des ADC
69
*/
70
71
int main (void) {
72
73
    PORTB |= (1<<PB2) | (1<<PB3) | (1<<PB0);                        //setzen der internen Pull-up-Widerstände 
74
  DDRB  &= ~( (1<<DDB2) | (1<<DDB3) | (1<<DDB0) );                //PB0, PB2 und PB3 als Eingänge definert
75
    DDRB  |= (1<<PB1);                                              //setzte PB1 von Port B als Ausgang
76
    DDRB  |= (1<<PB5);                                              //setzte PB5 von Port B als Ausgang
77
78
    DDRC  |= (1<<DDC5);                                             //setze Pin5 von PORTC als Ausgang
79
80
  ADMUX   = (1<<REFS0);                                           //nutze AVCC als Referenz (5V)
81
  ADCSRA  = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADIE);     //Teilungsfaktor 128 = 20Mhz/128=156,25kHz / Eingang 0
82
  ADCSRA |= (1<<ADEN);                                            //ADC aktivieren
83
  ADCSRA |= (1<<ADSC);
84
85
  
86
  TCCR1A = (1<<WGM11)|(1<<COM1A1);                                //Fast PWM, Zählweite einstellbar durch ICR1 
87
  TCCR1B = (1<<WGM12)|(1<<WGM13)|(1<<CS10);                       // Teiler 1==> 20Mhz/512==> 39062,5kHz
88
  ICR1   = 512;                                                   //Frequenz 39,06kHz
89
90
  TCCR2B = (1<<CS20) | (1<<CS21);//  | (1<<CS22) ;                //Teiler 32==> 625kHz==>625000/255=2450Hz
91
  TIMSK2 = (1<<TOIE2);                                            //Interrupt enable
92
93
94
   /************************************* Zähler für Strombegrenzung (Netzteil)*********************************************/
95
    
96
  TCCR0A = (1<<WGM00) | (1<<WGM01) | (1<<COM0A1);                 //Fast PWM, Top 0xFF
97
    TCCR0B = (1<<CS00);                                             //kein Prescaler, Frequenz 20MHz/255 = 78,3kHz
98
    DDRD  |= (1 << PD6 );                                           //setzte PD6 von Port D als Ausgang
99
100
   /************************************************************************************************************************/
101
102
  sei ();                                                         //global Interrupt enable
103
104
  while(1){
105
  }
106
107
}

von Uwe (de0508)


Lesenswert?

Hallo,

deine ISR bildet keine arithmetischer Mittelwert:
1
mittelwert += SPG_Wert;      // Filterung der Eingangswerte
2
  mittelwert /= 2;

Das ist eine arithmetische Reihe.

Siehe:
1) http://de.wikipedia.org/wiki/Arithmetisches_Mittel
2) http://de.wikipedia.org/wiki/Mittelwert

.

von Stefan E. (sternst)


Lesenswert?

Wegen dieser Zeile
1
TIMSK2 = (1<<TOIE2);
hast du laufend Software-Resets. Der Fehlerbeschreibung nach immer 
irgendwo zwischen dem ersten und zweiten ADC-Interrupt.

von Avr N. (balze)


Lesenswert?

Hallo Uwe,

vielen Dank fuer Deinen Hinweis.

Alle die sich an dem falsch verwendeten Begriff stoeren, ersetzen bitte 
in Gedanken “mittelwert“ durch “HORST“.

MfG,

Bakze aka AVR Noob

von Knut (Gast)


Lesenswert?

Warum hast du das Starten deiner Messung auskommentiert? So hast du nur 
eine Messung?

> OCR1A = pgm_read_word( &table1[mittelwert] );      // Kennlinienwert am
> gefilterten Eingangswert

Versuch mal Folgendes:
1
ISR(TIMER2_OVF_vect)
2
{
3
  ADCSRA |= (1<<ADSC);  Wandlung starten
4
}
5
6
7
ISR(ADC_vect) 
8
{
9
  mittelwert +=1;
10
  if (mittelwert > 511) {mittelwert = 0}; 
11
  OCR1A = mittelwert;
12
}
13
14
Hier solltest du sehen können wie der Tastgrad steigt.

Knut

von Avr N. (balze)


Lesenswert?

Guten Morgen zusammen,

Stefan Ernst schrieb:
> Wegen dieser Zeile
1
> TIMSK2 = (1<<TOIE2);
> hast du laufend Software-Resets. Der Fehlerbeschreibung nach immer
> irgendwo zwischen dem ersten und zweiten ADC-Interrupt.

äähmmmmm, OK?!
Koenntest Du mir das bitte erlaeuten?
Ich kann im Datenblatt keinen Hinweis finden, dass das so ist.
(Was ja nicht heisst, dass es nicht so ist. Ich bin schliesslich der AVR 
Noob :)

Knut schrieb:
> Warum hast du das Starten deiner Messung auskommentiert? So hast du nur
> eine Messung?
>
>> OCR1A = pgm_read_word( &table1[mittelwert] );      // Kennlinienwert am
>> gefilterten Eingangswert
>
> Versuch mal Folgendes:
>
>
1
> ISR(TIMER2_OVF_vect)
2
> {
3
>   ADCSRA |= (1<<ADSC);  Wandlung starten
4
> }
5
> 
6
> 
7
> ISR(ADC_vect)
8
> {
9
>   mittelwert +=1;
10
>   if (mittelwert > 511) {mittelwert = 0};
11
>   OCR1A = mittelwert;
12
> }
13
> 
14
> Hier solltest du sehen können wie der Tastgrad steigt.
15
>
>
> Knut

Das ist deshalb auskommentiert, weil der ADC so wie er konfiguriert ist 
doch im "free running" mode laeuft (oder bin ich auch hier schief 
gewickelt?)

Danke fuer Eure Unterstuetzung.

MfG,

Balze aka AVR Noob

von Stefan E. (sternst)


Lesenswert?

Avr Noob schrieb:
> Ich kann im Datenblatt keinen Hinweis finden, dass das so ist.

Das ist ja auch keine Frage der Hardware (Datenblatt), sondern der 
Software (AVRlibc).

Wenn ein Interrupt ausgelöst wird, für den es keine ISR gibt, wird eine 
Default-ISR ausgeführt, die einfach nur einen Software-Reset macht.

Ergo: Timer2 läuft über = Programm startet von vorn

von Avr N. (balze)


Lesenswert?

Hallo,

danke fuer die Erlaeuterung.
Ich war mir nicht bewusst, dass AVRlibc so mit nicht zuortbaren 
Interrupts umgeht.

Eine wichtige, neue Erkenntnis.
Es ist nich ein langer Weg weg vom AVR Noob :)

MfG,

Balze still aka AVR Noob

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.