Forum: Compiler & IDEs ATMEGA128: AD-Wandlung - Interrupt


von müllo (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte gerne vier Kanäle (MUX: 00000, 00001, 00010 und 00011)
nacheinander wandeln. Eine Lösung mittels Flagauswertung funktioniert
einwandfrei. Diesen Quelltext (wichtige Auszüge der einzelnen c-Files)
habe ich per Textdatei eingefügt. Bitte nicht gleich die Art und Weise
meiner ersten "modularen" Programmierung bemängeln -> wird bestimmt
noch besser ;-)))

Nun zum eigentlichen Problem. Ich möchte den Controller nicht jeder
Conversion im Leerlauf warten lassen. Deshalb die Idee, eine Wandlung
(Single-Conversion) anstoßen und das Ergebnis via ISR auszulesen.
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h>
4
#include <avr/signal.h>
5
6
#include <main.h>
7
8
9
//initalize values
10
volatile uint8_t AdcChannel = 0;
11
volatile uint8_t AdcBuffer[3] = {0x00,0x00,0x00,0x00};
12
13
int main(void)
14
{    
15
  InitTimer16();  //PWM at PORTB7...5 for Buffer 0..2 -> is okay
16
  DDRC = 0xFF;  //für Buffer 3 -> is okay
17
  
18
  while(1)
19
  {    
20
    
21
    if(AdcChannel > 3) AdcChannel = 0;
22
    
23
    AdcConv(AdcChannel);
24
    
25
    //test
26
    OCR1AL = AdcBuffer[0]; 
27
    OCR1BL = AdcBuffer[1];
28
    OCR1CL = AdcBuffer[2];
29
    PORTC = AdcBuffer[3]; 
30
    }
31
  return(0);
32
}
33
34
--------------------------------------------------------------------------------
35
36
#ifndef __MAIN_H
37
#define __MAIN_H
38
39
40
//Prototype
41
extern void AdcConv(uint8_t);    //set ch and start a conversation
42
extern void InitTimer16(void);    //Initialize Timer
43
44
//Variables
45
//Analog Inputs Channel 0...3
46
extern volatile uint8_t AdcChannel;  //counter for adc channels 
47
extern volatile uint8_t AdcBuffer[3];
48
49
#endif
50
51
--------------------------------------------------------------------------------
52
53
/*AdcConv*/
54
55
void AdcConv(uint8_t mux)
56
{
57
  ADMUX = mux;      //channel
58
  ADMUX |= (1<<ADLAR);    //left adjust result
59
  ADMUX |= (1<<REFS0);    //Vref to AVcc with capacitor
60
  ADCSRA = 0xC0;      //start conversion
61
}
62
63
--------------------------------------------------------------------------------
64
65
/*ISR_M128*/
66
67
//ISR
68
SIGNAL(SIG_ADC)
69
{
70
  cli();
71
  ADCSRA &= ~(1<<ADEN);    //disable ADC
72
  AdcBuffer[AdcChannel] = ADCH;  //save ADC result
73
  AdcChannel++;
74
  sei();
75
}

Leider wird dabei nur die erste Wandlung gemacht und danach wird's
Müll. Irgendwie vermute ich das Problem zwischen Aufruf der Funktion
AdcConv[AdcChannel] und der Wertzuweisung in der ISR.

Hat jemand einen Tipp, wie ich dies besser realisieren könnte?

Viele Grüße
müllo

von Alex (Gast)


Lesenswert?

sei() und cli() in der ISR sind überflüssig.

Ausserdem deaktivierst du in der ISR den ADC, so what?

Die liest nur die oberen 2 Bit des AD-Wertes aus, Absicht?

von müllo (Gast)


Lesenswert?

Ich kann den "Kanalwechsel" nur sicher ohne Ergebnisverlust machen,
wenn der ADC aus ist. Meine Idee: In der Funktion wird der ADC mit
gewünschtem Kanal gestartet und wenn Ergebnisinterrupt aktiv wird,
erfolgt die Deaktivierung. Mein Ergebnis (brauche statt der 10Bit nur
8Bit) wird linksseitig mittels gesetztem Steuerbit in ADCW geschrieben,
sodass mir die oberen 8Bit (ADCH) reichen.

Habe mir gestern noch eine Änderung einfallen lassen, die das Problem
evtl. lösst. -> komme aber am Di. an die µC-Hardware. Vielleicht kann
jemand mal über den Code fliegen und mir seine Meinung schreiben.
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h>
4
#include <avr/signal.h>
5
6
#include <main.h>
7
8
9
//initalize values
10
volatile uint8_t AdcChannel = 0;
11
volatile uint8_t AdcBuffer[3] = {0x00,0x00,0x00,0x00};
12
13
int main(void)
14
{    
15
  InitTimer16();  //PWM at PORTB7...5 for Buffer 0..2 -> is okay
16
  DDRC = 0xFF;  //für Buffer 3 -> is okay
17
  
18
  while(1)
19
  {    
20
    
21
    AdcConv(AdcChannel);  //Conversion of actual Channel 
22
    
23
    //test
24
    OCR1AL = AdcBuffer[0]; 
25
    OCR1BL = AdcBuffer[1];
26
    OCR1CL = AdcBuffer[2];
27
    PORTC = AdcBuffer[3]; 
28
    }
29
  return(0);
30
}
31
32
--------------------------------------------------------------------------------
33
34
#ifndef __MAIN_H
35
#define __MAIN_H
36
37
38
//Prototype
39
extern void AdcConv(uint8_t);    //set ch and start a conversation
40
extern void InitTimer16(void);    //Initialize Timer
41
42
//Variables
43
//Analog Inputs Channel 0...3
44
extern volatile uint8_t AdcChannel;  //counter for adc channels 
45
extern volatile uint8_t AdcBuffer[3];
46
47
#endif
48
49
--------------------------------------------------------------------------------
50
51
/*AdcConv*/
52
53
void AdcConv(uint8_t mux)
54
{
55
  ADMUX = mux;      //Channel
56
  ADMUX |= (1<<ADLAR);    //left adjust result
57
  ADMUX |= (1<<REFS0);    //Vref to AVcc with capacitor
58
  ADCSRA = 0xC0;      //Start single Conversion
59
}
60
61
--------------------------------------------------------------------------------
62
63
/*ISR_M128*/
64
65
//ISR
66
SIGNAL(SIG_ADC)
67
{
68
  cli();
69
  ADCSRA &= ~(1<<ADEN);    //disable ADC
70
  AdcBuffer[AdcChannel] = ADCH;  //save ADC result
71
  if (AdcChannel<3) AdcChannel++;
72
  else AdcChannel=0;
73
  sei();
74
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Ich kann den "Kanalwechsel" nur sicher ohne Ergebnisverlust machen,
> wenn der ADC aus ist.

Wer erzählt denn sowas?  Du kannst den Kanalwechsel jederzeit
programmieren.  Ausgeführt wird er erst nach dem Ende der laufenden
Wandlung.  Damit kann man in lückenloser Reihenfolge Kanäle scannen.

von müllo (Gast)


Lesenswert?

Hallo Jörg,

ich habe gerade nochmal im DB vom Atmega128 nachgeschaut und du hast
natürlich Recht. Auf Seite 236 des wird dem Programmierer der Ratschlag
gegeben, keinen neuen Kanal bzw. neue Referenz innerhalb des ersten
Zyklus nach Setzen des Bit ADSC zu wählen. Den Satz hatte ich wohl
Querlesen nicht richtig im Kontext gelesen.

Meinen Quelltext habe ich jetzt so geändert, dass ich außerhalb der
while(1)-Loop in der main.c eine Single- AD- Wandlung mit der Funktion
"ADC_Conv(0);" aufrufe.
1
ADC_Conv(uint8_t adc_channel)
2
{
3
  ADMUX = adc_channel;          //channel
4
  ADMUX |= (1<<ADLAR);      //left adjust result
5
  ADMUX |= (1<<REFS0);      //Vref to AVcc with capacitor
6
  ADCSRA = 0xC0;          //start conversion
7
}

Den Rest macht die ISR und startet dann durchgehend die restlichen
Kanäle per Single-Conversion durch.
1
SIGNAL(SIG_ADC)
2
{
3
  volatile uint8_t adc_channel;          
4
  uint8_t tmp_sreg = SREG;
5
  cli();
6
  
7
  adc_buffer[adc_channel] = ADCH;    //save result of actual
8
AD-Conversion
9
  if (adc_channel<3) adc_channel++;
10
  else adc_channel=0;
11
  ADC_Conv(adc_channel);      //starting a new single conversion
12
  
13
  SREG = tmp_sreg;
14
}

Wenn jetzt noch ein richtiger Bug vorhanden ist, freue ich mich über
Hinweise, natürlich auch Kritiken zum Quellcode.

mfg

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.