Forum: Mikrocontroller und Digitale Elektronik ADC per Interupt am ATmega328 auslesen


von Peter S. (petershaw)


Lesenswert?

Hallo,

ich bin kurz vor der verzweifelten Aufgabe. Weiß aber das meine letzte 
Hoffnung nach Stunden des Googlens, Lesens und Versuchens, wenn - dann 
ihr seit.

Ich schreibe gerade ein Programm von mir um, das den ADC-Value 
kontinuierlich ausließt und auf einem LCD darstellt. Diesen Programm 
funktioniert so weit wie gewollt.
Jetzt schreibe ich es so um, das der MainLoop den ADC nicht ständig neu 
abfragt, sondern die Wert-Variable im Interupt gesetzt wird.

Mein Code ließt den Wert leider nur genau 1x am Anfang aus! nicht mehr, 
nicht weniger. Wie bekomme ich es hin, das bei jedem Wechsel der 
anliegenden Spannung der ISR ausgeführt wird? Was muss ich setzen damit 
das funktioniert.
Entweder mein LED_RED leuchtet dauer und er kommt gar nicht mehr aus dem 
ISR raus, oder er ließt den Wert nur ein mal ein.

Ich habe jetzt versucht das über einen Timer zu lösen. Anbei der Code, 
währe schön wenn ihr dazu was sagen könnt und mich auf die richtige 
Fährte lotst.
1
 
2
/**
3
 * ADCdisplay displays the value of the adc :-)
4
 * version 2 - uses ISR
5
 *
6
 * Outupt on a 2x16 LCD:
7
 * ----------------
8
 * ADC-Value
9
 * -> 223/222/224
10
 * ----------------
11
 * which is -> current / min / max 
12
 *
13
 */
14
15
#define F_CPU 16000000UL                // Clock speed
16
17
#define LED_RED     0b00000001          // PIN Register for the red led
18
#define LED_GREEN   0b00000010          // PIN Register for the green led
19
20
#include <stdlib.h>
21
#include <avr/io.h>
22
#include <avr/interrupt.h>
23
#include <util/delay.h>
24
#include "lcd.h"
25
26
volatile int value = 0;                 // current value
27
int last_value = 0;                     // value from the last messurement
28
int biggest = 0;                        // max value
29
int lowest = 1023;                      // min value
30
31
char buf[4];                            // a buffer for the value to disply
32
33
/**
34
 * On the dev-board there are two
35
 * static led's a red one and a
36
 * green one.
37
 * This routine flashes one off
38
 * the static leds.
39
 * ----------------------------         //
40
 *
41
 */
42
void blink_led(int led){                // toggle a PIN on PortB for any blink call
43
    PORTB |= led;                       // LED on
44
    _delay_ms(150);
45
    PORTB &= ~led;                      // toggle LED off
46
    _delay_ms(300);
47
    return;
48
}
49
50
/**
51
 * Interupt calld by change in 
52
 * ADC register
53
 * ----------------------------         //
54
 *
55
 */
56
ISR(ADC_vect)
57
{
58
  blink_led(LED_GREEN);               // tell that the conversation is done
59
    value = ADCW;
60
}
61
62
/**
63
 * Enable and configure
64
 * the analog-digital converter
65
 * ----------------------------         //
66
 *
67
 */
68
void initADC(channel){
69
    
70
    TCNT0=0x00;                         // set timer.0 to initial 0
71
    TCCR0B = (1<<CS02);                 // start timer0 with /256 prescaler
72
    
73
    ADCSRA = (1<<ADEN)                  // enable ADC
74
    | (1<<ADIE)                         // enable interupt
75
    | (1<<ADATE)                        
76
    | (1<<ADPS2)                        // set 128 to the prescaller
77
    | (1<<ADPS1)
78
    | (1<<ADPS0);
79
    
80
    ADCSRB = ((1<<ADTS2)                // set timer.0 overflow
81
    | (0<<ADTS1)
82
    | (0<<ADTS0));
83
    
84
  ADMUX = channel | (1<<REFS0);       // set the channel
85
86
}
87
88
/**
89
 * Main routine
90
 * ----------------------------         //
91
 *
92
 */
93
int main(void){
94
    DDRB = 0b00000011;                  // set up pin direction PortB as Output
95
    blink_led(LED_RED);
96
    
97
    lcd_init(LCD_DISP_ON);              // initialize display, cursor off
98
    
99
    lcd_puts("*** WELCOME ***\n");      // welcome message
100
    lcd_puts(" - ADCV  V.2 - ");
101
    _delay_ms(2000);                    // wait for 2sec
102
103
    lcd_clrscr();                       // clear display and home cursor
104
    lcd_puts("ADC-Value\n");            // show the mode
105
    lcd_puts("->");                     // show the indicator
106
    
107
108
    initADC(5);
109
    
110
    sei();
111
    while(1) {                          // Mainloop
112
        blink_led(LED_RED);
113
114
        if(value != last_value){        // if something has changes
115
116
            itoa(value, buf, 10);       // convert the int to string buffer
117
        
118
            lcd_home();                 // display cleanup
119
            lcd_gotoxy(2,1);
120
            lcd_puts("              ");
121
            lcd_home();
122
            lcd_gotoxy(2,1);
123
            lcd_puts(buf);              // show current value
124
            
125
            if(value > biggest){        // get min and max
126
                biggest = value;
127
            }
128
            if(value < lowest){
129
                lowest = value;
130
            }
131
            
132
            lcd_puts("/");
133
            itoa(lowest, buf, 10);
134
            lcd_puts(buf);              // show min
135
            
136
            lcd_puts("/");
137
            itoa(biggest, buf, 10);
138
            lcd_puts(buf);              // show max
139
            
140
            last_value = value;         // remember the last value
141
            
142
        }
143
    }
144
}

Dankeschön.

von Michael (Gast)


Lesenswert?

Peter Shaw schrieb:
> Entweder mein LED_RED leuchtet dauer und er kommt gar nicht mehr aus dem
> ISR raus

Was erwartest du, wenn du den µC über eine Ewigkeit von 450ms in der ISR 
ankettest.

von Peter S. (petershaw)


Lesenswert?

Hallo Karl Heinz,

erstmal danke. Und ja, onChange() Implementierung währe seltsam, das 
gebe ich zu.
In meinem Code habe ich während der Initialisierung in initADC() einen 
Timer gestartet:
1
    TCNT0=0x00;                         
2
    TCCR0B = (1<<CS02);

und dachte das ich den Overflow des Timerst mit diesen Zeilen nutze um 
das encoden zu starten:
1
ADCSRB = ((1<<ADTS2)
2
    | (0<<ADTS1)
3
    | (0<<ADTS0));

Du sagst ja auch, das ich ihn von anderen Quellen triggern lassen kann, 
also auch vom Timer.0, oder?

Mit meiner Hand auf meinem Herz: ich will das lösen können!

von Karl H. (kbuchegg)


Lesenswert?

Aus dem Datenblatt
1
A conversion
2
will be triggered by the rising edge of the selected Interrupt Flag.

d.h. das Overflow Interrupt Flag des Timers, bzw. dessen Veränderung von 
0 auf 1 triggert die Wandlung. Dieses Flag setzt sich aber nicht von 
alleine zurück. D.h. du musst zusätzlich noch das Overflow Flag des 
Timers löschen, denn ansonsten findet ja keine Veränderung dieses Flags 
mehr statt. Wie auch immer du das machen willst.
Zb mit einer ISR für den Timer Overflow, die sonst nichts tut. Zb indem 
du nach einer ADC Wandlung in der ADC-ISR dieses Overflow Flag händisch 
löscht (Achtung: Das ist ein Interrupt Flag. D.h. es wird durch 
einschreiben einer 1 gelöscht!)

von delay im isr (Gast)


Lesenswert?

du wartest 500ms waehrend der interruptausfuehrung.... Das kann nicht 
klappen

von Peter S. (petershaw)


Lesenswert?

hallo Michael,


ach f****,
wenn ich das raus nehme und im ISR neu starte, klappte.
1
ISR(ADC_vect)
2
{
3
   value = ADCW;
4
    ADCSRA |= (1<<ADSC);
5
}

Aber noch eine Frage: waru muss ich den neu starten, reicht der Timer 
Overflow nicht aus?

Danke.

hand auf Kopf hau

von Peter S. (petershaw)


Lesenswert?

Ok,
danke. Jetzt hab auch ich es verstanden ^^.

Vielen lieben Dank.

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.