Forum: Mikrocontroller und Digitale Elektronik A/D-Wandler Kanalumschaltung beim ATMEGA 16 mit „case“ Auswahl


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Jenny L. (jenny_lo)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

mich beschäftigt schon eine Weile ein Problem, bei dem ich ohne Hilfe 
wohl nie den Fehler finde.

Ich habe an einen ATMEGA 16 unter anderem 6 Potis angeschlossen, um mit 
Hilfe des A/D Wandlers Analogwerte zur weiteren Verarbeitung in einem 
Programm vorzugeben (Endstellungen für Servos). Die Hardware ist richtig 
aufgebaut und die Kanäle arbeiten einzeln getestet auch richtig. Das 
Auslesen zweier Kanäle geht auch, wenn ich die auskommentierte if – else 
Konstruktion verwende.  Für das Auslesen aller 6 Kanäle in der 
Interruptroutine würde es aber sehr unübersichtlich. Deshalb habe ich 
eine case Auswahl zur Umschaltung eingesetzt. Aber diese tut einfach 
nicht. Ich bin mir sicher, dass ich alle anderen Fehlerquellen 
ausgeschlossen habe und es nur daran liegen kann. Einen Fehler kann ich 
aber nicht entdecken. Langsam frage ich mich, ob es überhaupt möglich 
ist, in der Interruptroutine eine case Auswahl einzusetzen. Allerdings 
habe ich auch nirgends einen Hinweis gefunden, dass es da 
Einschränkungen geben könnte. Mittlerweile bin ich ratlos.

Es wäre nett, wenn Ihr mal einen Blick auf den Code werfen könntet. 
Vielleicht übersehe ich ja die ganze Zeit etwas.

von frank (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Als C Datei anhängen oder als Code einbinden.
Ansonsten geht das Highlighting nicht ;-)
#include <avr/io.h>
#include <avr/interrupt.h>
#include "global.h"
#include <stdbool.h> // bool einbinden


//AD-Wandler

static unsigned char I=1; //Schleifenzähler
static unsigned char A=0; //Merker für Kanalumschaltung
static unsigned int ADCwert=0; //Wert AD-Wandler



ISR (ADC_vect) //Interruptroutine für AD Wandlung, wird aufgerufen, wenn ein neues Ergebnis vom AD-Wandler vorliegt
{
  
  unsigned int schmier=0; //Schmiervariable

  //AD-Wandlung

  //schmier = ADCH; //die 8 höchsten Bits des Ergebnisses abholen (0...255)
  schmier = ADCL;
  schmier += (ADCH<<8);

  ADCwert=ADCwert+schmier; //Aufsummieren
  I=I+1;
    
    if (I == 20)
    {
      ADCwert=ADCwert/I; //Mittelwert berechnen
      I=1;

      //Wert übergeben
      
      uiADC[A] = ADCwert; //Wert an Übergabefeld
      ADCwert=0;
      
      //Kanalwechsel

      switch (A)
      {
        case 0:

          ADMUX |=(1<<MUX0); //MUX0 setzen für Kanal 1
          A=1; //Kanalmerker auf 1
          break;

        case 1:

          ADMUX &=~(1<<MUX0); //MUX0 rücksetzen und
          ADMUX |=(1<<MUX1); //MUX1 setzen für Kanal 2
          A=2; //Kanalmerker auf 2 setzen
          break;

        case 2:

          ADMUX |=(1<<MUX0); //MUX0 zusätzlich zu MUX1 setzen für Kanal 2
          A=3; //Kanalmerker auf 3
          break;

        case 3:

          ADMUX &=~(1<<MUX0); ADMUX &=~(MUX1); //MUX0 und MUX1 rücksetzen und
          ADMUX |=(1<<MUX2); //MUX2 setzen für Kanal 4
          A=4; //Kanalmerker auf 4
          break;

        case 4:

          ADMUX |=(1<<MUX0); //MUX0 zusätzlich zu MUX2 setzen für Kanal 5
          A=5; //Kanalmerker auf 5
          break;

        case 5:

          ADMUX &=~(1<<MUX0); ADMUX &=~(MUX2); //MUX0 und MUX2 rücksetzen für Kanal 0
          A=0; //Kanalmerker auf 0
          break;

      }

      /*
      if (A==0) //wenn Kanal 0
      {
        ADMUX |=(1<<MUX0); //MUX0 setzen für Kanal 1
        A=1; //Kanalmerker auf 1
      }
      
      else //wenn Kanal 1
      {
        ADMUX &=~(1<<MUX0); //MUX0 rücksetzen für Kanal 0
        A=0; //Kanalmerker auf 0
      }
*/
    }
  
  //ADC neu starten
  
  ADCSRA |=(1<<ADSC);
  
}

von Jenny L. (jenny_lo)


Bewertung
0 lesenswert
nicht lesenswert
Danke für den Tipp. Ich wusste nicht, dass das geht. Ich schreibe zu 
selten.

von Frank (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Jenny Lo. schrieb:
> Deshalb habe ich
> eine case Auswahl zur Umschaltung eingesetz

Kann man machen, würde ich aber nicht.

Jenny Lo. schrieb:
> Langsam frage ich mich, ob es überhaupt möglich
> ist, in der Interruptroutine eine case Auswahl einzusetzen

Doch, geht. Wieso auch nicht.

Ich kann es nicht ausprobieren hier, aber ich hätte es mal so wie im 
Anhang probiert.

von Frank (Gast)


Bewertung
0 lesenswert
nicht lesenswert
>uiADC[adcCh]
Das stimmt natürlich nicht.
>uiADC[adcCh%8]
So ist es richtiger.

von Route_66 H. (route_66)


Bewertung
0 lesenswert
nicht lesenswert
Frank schrieb:
> So ist es richtiger.

...und wie ist es dann "am richtigsten"?

von Fritz G. (fritzg)


Bewertung
0 lesenswert
nicht lesenswert
Wenn man in einer ISR globale Variable verwendet, müssen die mit 
volatile versehen werden.
Wenn du außerhalb auf adcwert zugreifst, muss es mit von cli und sei 
umgeben werden, sonst ist der Wert manchmal falsch.

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Bewertung
1 lesenswert
nicht lesenswert
Ich nehme direkt ADMUX im switch-case Konstrukt, mach mir allerdings 
vorher ein paar #defines, um das ganze übersichlich zu halten:
//in 'setup.h'
//! The ADC channel where the analog speed reference is connected.
#define ADC_CHANNEL_SPEED_REF   3
//! The ADC channel where the motor current measuring amp is connected.
#define ADC_CHANNEL_CURRENT     4
//! The spare ADC channel ( e.g. heatsink temperatur or battery volts )
#define ADC_CHANNEL_TEMP     5
//! ADC clock prescaler 2 setting.
#define ADC_PRESCALER_2        ((0 << ADPS2) | (0 << ADPS1) | (1 << ADPS0))
//! ADC clock prescaler 4 setting.
#define ADC_PRESCALER_4        ((0 << ADPS2) | (1 << ADPS1) | (0 << ADPS0))

//! ADC clock prescaler 8 setting.
#define ADC_PRESCALER_8        ((0 << ADPS2) | (1 << ADPS1) | (1 << ADPS0))

//! ADC clock prescaler 16 setting.
#define ADC_PRESCALER_16        ((1 << ADPS2) | (0 << ADPS1) | (0 << ADPS0))
//! ADC clock prescaler 64 setting.
#define ADC_PRESCALER_64        ((1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0))
//! ADC clock prescaler used in this application.
#define ADC_PRESCALER           ADC_PRESCALER_64

//! ADC internal voltage reference channel value.
#define ADC_REFERENCE_VOLTAGE_INTERNAL      ((1 << REFS1) | (1 << REFS0))

//! ADC VCC voltage reference channel value.
#define ADC_REFERENCE_VOLTAGE_VCC           ((0 << REFS1) | (1 << REFS0))

//! ADC AREF voltage reference channel value.
#define ADC_REFERENCE_VOLTAGE_AREF          ((0 << REFS1) | (0 << REFS0))

/*! ADC voltage reference used in this application.
 *
 *  \todo Select ADC voltage reference channel.
 */
#define ADC_REFERENCE_VOLTAGE     ADC_REFERENCE_VOLTAGE_VCC

//! ADMUX settings for analog speed reference measurement.
#define ADMUX_SPEED_REF   (ADC_REFERENCE_VOLTAGE | (1 << ADLAR) | (ADC_CHANNEL_SPEED_REF << MUX0))

//! ADMUX settings for motor current measurement.
#define ADMUX_CURRENT     (ADC_REFERENCE_VOLTAGE | (1 << ADLAR) | (ADC_CHANNEL_CURRENT << MUX0))

//! ADMUX settings for spare ad channel (heatsink temp).
#define ADMUX_TEMP    (ADC_REFERENCE_VOLTAGE | (1 << ADLAR) | (ADC_CHANNEL_TEMP << MUX0))

// in main.c
// globals
volatile uint8_t current,speedInput,temperature;
ISR(ADC_vect)
{
  switch (ADMUX)
  {
  case ADMUX_SPEED_REF:
      speedInput = (speedInput + ADCH) >> 1; // do some averaging
      ADMUX = ADMUX_CURRENT;
    break;
  case ADMUX_CURRENT:
      current = ADCH;
      ADMUX = ADMUX_TEMP; break;
  case ADMUX_TEMP:
  temperature = ADCH;
  ADMUX = ADMUX_SPEED_REF; 
  if (temperature > MOTOR_MAX_TEMPERATURE) error |= ERR_OVERHEAT; break;
  default:
    //This is probably an error and should be handled.
    ADMUX = ADMUX_SPEED_REF; 
    break;
  }
  
// restart the ADC
  ADCSRA = (1 << ADEN) | (1 << ADSC) | (0 << ADATE) | (1 << ADIF) | (1 << ADIE) | ADC_PRESCALER;

}

: Bearbeitet durch User
von Ralf G. (ralg)


Bewertung
0 lesenswert
nicht lesenswert
  //schmier = ADCH; //die 8 höchsten Bits des Ergebnisses abholen (0...255)
  schmier = ADCL;
  schmier += (ADCH<<8);

  ADCwert=ADCwert+schmier; //Aufsummieren
  I=I+1;

Das ist, glaube ich, nicht so gewollt.
0. Du rechnest mit ADCH, holst das Ergebnis aber nicht ab.
1. Aus dem 'nicht gezeigten Code-Teil' erahne ich: Du brauchst nur eine 
8-Bit-Wandlung. Dieses Ergebnis schiebst du um 8 Bit nach links und 
addierst es zu 'schmier'. Wenn's schlecht läuft, machst du das nur 
einmal!
(Hinweis: 255 * 256 = 65280)
2. Es reicht, ADCH zu lesen. (Unter der Annahme von Punkt 1.)
//AD-Wandler

ISR (ADC_vect)
{  
  static uint8_t I=1;        //Schleifenzähler
  static uint8_t A=0;        //Merker für Kanalumschaltung
  static uint16_t ADCwert=0; //Wert AD-Wandler

  //AD-Wandlung

  ADCwert += ADCH; //Aufsummieren
  I=I+1;
 
  ....
3. Falls ich jetzt nur nicht durch deine Berechnungen mit 'schmier' 
durchgestiegen sein sollte, musst du trotzdem ADCH abholen. In C ganz 
einfach:
variable = ADC;

von Jenny L. (jenny_lo)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Recht vielen Dank für Eure Antworten. Auch wenn ich als 
Gelegenheitsprogrammiererin nicht alles verstanden habe, ist mir klar 
geworden, dass die Kanalumschaltung durch das Umschalten der einzelnen 
Bits im ADMUX Register Murks ist. Ich habe den Code so verändert, dass 
immer der passende Hex-Wert in das Register geschrieben wird und das 
Ganze mit ein paar "defines" lesbarer gemacht. Im Anhang das Ergebnis.

Nachmals vielen Dank für die Hilfe.

: Bearbeitet durch User

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.