mikrocontroller.net

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


Autor: müllo (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

#include <main.h>


//initalize values
volatile uint8_t AdcChannel = 0;
volatile uint8_t AdcBuffer[3] = {0x00,0x00,0x00,0x00};

int main(void)
{    
  InitTimer16();  //PWM at PORTB7...5 for Buffer 0..2 -> is okay
  DDRC = 0xFF;  //für Buffer 3 -> is okay
  
  while(1)
  {    
    
    if(AdcChannel > 3) AdcChannel = 0;
    
    AdcConv(AdcChannel);
    
    //test
    OCR1AL = AdcBuffer[0]; 
    OCR1BL = AdcBuffer[1];
    OCR1CL = AdcBuffer[2];
    PORTC = AdcBuffer[3]; 
    }
  return(0);
}

--------------------------------------------------------------------------------

#ifndef __MAIN_H
#define __MAIN_H


//Prototype
extern void AdcConv(uint8_t);    //set ch and start a conversation
extern void InitTimer16(void);    //Initialize Timer

//Variables
//Analog Inputs Channel 0...3
extern volatile uint8_t AdcChannel;  //counter for adc channels 
extern volatile uint8_t AdcBuffer[3];

#endif

--------------------------------------------------------------------------------

/*AdcConv*/

void AdcConv(uint8_t mux)
{
  ADMUX = mux;      //channel
  ADMUX |= (1<<ADLAR);    //left adjust result
  ADMUX |= (1<<REFS0);    //Vref to AVcc with capacitor
  ADCSRA = 0xC0;      //start conversion
}

--------------------------------------------------------------------------------

/*ISR_M128*/

//ISR
SIGNAL(SIG_ADC)
{
  cli();
  ADCSRA &= ~(1<<ADEN);    //disable ADC
  AdcBuffer[AdcChannel] = ADCH;  //save ADC result
  AdcChannel++;
  sei();
}


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

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: müllo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

#include <main.h>


//initalize values
volatile uint8_t AdcChannel = 0;
volatile uint8_t AdcBuffer[3] = {0x00,0x00,0x00,0x00};

int main(void)
{    
  InitTimer16();  //PWM at PORTB7...5 for Buffer 0..2 -> is okay
  DDRC = 0xFF;  //für Buffer 3 -> is okay
  
  while(1)
  {    
    
    AdcConv(AdcChannel);  //Conversion of actual Channel 
    
    //test
    OCR1AL = AdcBuffer[0]; 
    OCR1BL = AdcBuffer[1];
    OCR1CL = AdcBuffer[2];
    PORTC = AdcBuffer[3]; 
    }
  return(0);
}

--------------------------------------------------------------------------------

#ifndef __MAIN_H
#define __MAIN_H


//Prototype
extern void AdcConv(uint8_t);    //set ch and start a conversation
extern void InitTimer16(void);    //Initialize Timer

//Variables
//Analog Inputs Channel 0...3
extern volatile uint8_t AdcChannel;  //counter for adc channels 
extern volatile uint8_t AdcBuffer[3];

#endif

--------------------------------------------------------------------------------

/*AdcConv*/

void AdcConv(uint8_t mux)
{
  ADMUX = mux;      //Channel
  ADMUX |= (1<<ADLAR);    //left adjust result
  ADMUX |= (1<<REFS0);    //Vref to AVcc with capacitor
  ADCSRA = 0xC0;      //Start single Conversion
}

--------------------------------------------------------------------------------

/*ISR_M128*/

//ISR
SIGNAL(SIG_ADC)
{
  cli();
  ADCSRA &= ~(1<<ADEN);    //disable ADC
  AdcBuffer[AdcChannel] = ADCH;  //save ADC result
  if (AdcChannel<3) AdcChannel++;
  else AdcChannel=0;
  sei();
}


Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: müllo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
ADC_Conv(uint8_t adc_channel)
{
  ADMUX = adc_channel;          //channel
  ADMUX |= (1<<ADLAR);      //left adjust result
  ADMUX |= (1<<REFS0);      //Vref to AVcc with capacitor
  ADCSRA = 0xC0;          //start conversion
}

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

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

mfg

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.