Forum: Compiler & IDEs LED dimmen per PWM mit ADC (ATMega88)


von C-Neuling (Gast)


Lesenswert?

Das Programm soll eine Spannung die durch eine Led gemessen wird und per 
AD Wandler umgewandelt wird per PWM eine andere LED dimmen, je nach 
Größe der Spannung.
Die optische Led hängt an PC5.
Die Led welche gedimmt werden soll an PB2.
Kann jemand sagen welche Teile in diesem Programm nicht stimmen? Denn 
leider funtioniert es nicht... :-(

#include <stdlib.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>

void ad_init(void);
void wandeln(void);
void timer0(void);
void initialisierung(void);
void PWM(void);

#define TEILER1 5000  //??????????????
#define TEILER2 5000  //??????????????

unsigned int softwareteiler1, softwareteiler2;
unsigned int wert;

bool Takt1 =0;
bool Takt2 =0;


int main(void)
{
  initialisierung();  // ruft alle Initialisierungen auf

  while (1)          // Endlosschleife
  {
      if (Takt1)
      {
      Takt1=0;
      wandeln();
      }

    if (Takt2)
      {
      Takt2 = 0;
      PWM();

      }
  }

}



// Timer0 initialisieren


void timer0(void)
{
  sei();
  TCCR0B |= (1<<CS01);      //Prescaler  / 8
  TIMSK0 |= (1<<TOIE0);
}



// Initialiserung AD-Wandler


void ad_init(void)
{


PRR = 0x00;

MCUCR = 0x10;

ADCSRA |= (1<<ADEN); // A/D-Wandler Enable


ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);  // Prescaler auf 128 
stellen


ADMUX |= (1<<REFS0) | (1<<MUX2) | (1<<MUX0); //  (AVCC als AREF + 
external C) (ADC5 = input)


ADCSRB = 0x00;  // Free runing mode


DIDR0 = 0x3f;  // Digital Input Disable Register



}


// Interrupt Routine Timer0 Overflow


ISR (TIMER0_OVF_vect)
{
  softwareteiler1--;
  if (softwareteiler1 == 0)
    {
    softwareteiler1 = TEILER1;
    Takt1 = 1;                // Takt1 Flag = True
    softwareteiler2--;
    if (softwareteiler2 == 0)
      {
      softwareteiler2 = TEILER2;
      Takt2=1;              // Takt2 Flag = True
      }
    }

}


// WANDELN

void wandeln (void)
{


  ADCSRA |= (1<<ADEN);  // A/D Wandler einschalten

  // Dummy-Messung (eine Messung wird durchgeführt und dann verworfen)

  ADCSRA |= (1<<ADSC);                // eine ADC-Wandlung
    while ( ADCSRA & (1<<ADSC) );    // Auf Abschluss der Wandlung 
warten

  wert = ADCW;    // Dummy Messung
  wert = 0;

  ADCSRA |= (1<<ADEN);    //Free Running Modus wir eingeschaltet
  ADCSRB |= 0x00;
  wert=ADCW;              //Register wird ausgelesen und wert wird 
übergeben
}




//PWM

void PWM(void)
{
   DDRB = (1 << PB2 );
   TCCR2A = (1<<COM2A1) | (1<<WGM21)| (1<<WGM20);
   TCCR2B = (1<<WGM22) | (1<<CS00);
   ICR1 = 0x00FF;
   OCR2A = wert;

}


//Initialisierung in main


void initialisierung(void)
{


  softwareteiler1 = TEILER1; //Variablen konstanten Wert zuweisen
  softwareteiler2 = TEILER2;

  DDRB |= (1<<PB2);    // Port B2 als Ausgang (gelbe LED)
  DDRC &= ~(1<<PC5);  // Pin 5 an Port C als Eingang definieren (weise 
LED)

  timer0();  // Initialisierung Timer0
  ad_init();  // Initialisierung AD-Wandler


}

von Jörg X. (Gast)


Lesenswert?

- Takt1, Takt2 müssen 'volatile' werden - softwareteiler1 und 
softwareteiler2 wahrscheinlich auch.
 - Sind die TEILERx Macros geraten?
1
5000 * 5000 * 256 * 8
2
TEILER1
3
       TEILER2
4
              8-Bit -Timer
5
                     Prescaler
...sind eine Menge Takte zwischen zwei Wandlungen
 - Musst du den ADC immer ab- und wieder anschalten? Es reicht doch 
(wenn's nicht ultra-mobil werden soll) wenn der ADC bei der 
Initialisierung angeschaltet (ADEN-Bit) wird
 - bei "wandlung()" passen Bits und Kommentare nicht zusammen ("ADCSRA 
|= (1 << ADEN);  //Free Running Modus wir eingeschaltet")
 - Warum gibt "wandlung()" nicht einfach den gemessenen Wert zurück?

hth. Jörg

von C-Neuling (Gast)


Lesenswert?

Hallo Jörg danke das du dir die Mühe gemacht hast das Programm durch zu 
schauen.
Was meinst du mit Makros das verstehe ich nicht? Die 5000 hab ich 
einfach mal so reingetippt das was da steht ich weis noch nicht wie ich 
auf richtige Werte komme.
Ja es müsste okay sein wenn ich den ADC in der Initialisierung 
einschalte
das müsste ja dann so aussehen?

void initialisierung(void)
{


  softwareteiler1 = TEILER1; //Variablen konstanten Wert zuweisen
  softwareteiler2 = TEILER2;

  DDRB |= (1<<PB2);    // Port B2 als Ausgang (gelbe LED)
  DDRC &= ~(1<<PC5);  // Pin 5 an Port C als Eingang definieren (weise
LED)

  timer0();  // Initialisierung Timer0
  ad_init();  // Initialisierung AD-Wandler

   ADCSRA |= (1<<ADEN);  //ADC wird eingeschaltet
}

und diese Zeile dann überall rauslöschen?!

Wie kann ich bei wandlung()  nur den gemessen Wert zurück geben? Das hab 
ich noch nicht verstanden.


Sorry für so viele Fragen aber ist halt echt Neuland

von Jörg X. (Gast)


Lesenswert?

... Weiter im text:
 - Der PWM-ausgang, den du im Programm einschaltest (OC2A) liegt auf 
PB*3*, nicht PB2. Du musst also die LED anders anschließen oder OC*1B* 
benutzen.
 - Verrate doch bitte, wie die LED zu messung angeschlossen ist -- wenn 
die LED einfach so angeschlossen ist (direkt am ADC-pin), wirst du da 
nicht so wahnsinnig viel messen können.
 - wenn du 8-Bit-PWM benutzt, musst du den Wert anpassen (wert>>2, 
wert/4, ADLAR setzen und nur das ADCH-Register benutzen, etc.)
 - Es reicht auch den Timer am anfang einzustellen, und im laufenden 
Programm NUR noch das OCnx-Register zu verstellen.

hth. Jörg
ps.: sry wegen der 'Meckerei' ;)

von Jörg X. (Gast)


Angehängte Dateien:

Lesenswert?

Ich hab dein Programm mal nach meinem Geschmack umstrukturiert, es 
kompiliert (mit WinAVR-20071221), ist aber ungetestet.

Poste unbedingt deine Schaltung (auch wenn ich dir damit 
wahrscheinlich nicht wirklich helfen kann)

hth. Jörg
ps.: besorg dir ein C-Buch!
pps.: als Notlösung gibt's im Web ein Openbook über C, ich finde das 
aber ein bisschen unübersichtlich

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.