Forum: Mikrocontroller und Digitale Elektronik Atmega48 ADC zu PWM


von Ranjid (Gast)


Lesenswert?

Hallo, ich versuche gerade mittels ADC über PWM eine LED zu dimmen, das 
heißt die Helligkeit der LED hängt von der Helligkeit des Umgebungslicht 
ab.
Aber irgendwie wie will der ADC nicht so wie ich will. Der PWM 
funktioniert, den habe ich schon so getestet, aber der ADC will 
anscheinden nicht so.
AREF habe ich mit 100nF nach GND gelegt. Alles dürfte richtig beschaltet 
sein. Ich habe auch schon versucht die interne Spannung zu benutzen, hat 
aber auch nicht geklappt.

Hier mein Code
1
#define F_CPU 14745600UL
2
3
#include <avr/interrupt.h>
4
#include <avr/io.h>
5
#include <util/delay.h>
6
7
int main(void)
8
{
9
  uint16_t adc_value = 0;
10
  uint16_t pwm_calc_value = 0;
11
  
12
  DDRB |= (1<<PB1);
13
  DDRC &= ~(1<<PC0);
14
  PORTC |= (1<<PC0);
15
  
16
  TCCR1A |= ((1<<COM1A1) | (1<<WGM10));  //non inverting, Prescaler 256
17
  TCCR1B |= ((1<<WGM12) | (1<<CS12));  // Prescaler 256, Fast PWM
18
  OCR1A = 200;
19
  
20
  ADMUX |= (1<<REFS0);  //extern rev, adc0
21
  ADCSRA |= ((1<<ADEN) | (1<<ADIE) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2));  //adc enable, interrupt, prescaler 128
22
  //free running mode ADCSRB
23
  
24
  sei();
25
    while(1)
26
    {
27
        adc_value = ADCW;
28
        pwm_calc_value = adc_value / 5;
29
        OCR1A = pwm_calc_value;
30
        _delay_ms(50);
31
    }
32
}

ADCW wird erstmal durch 5 geteilt, damit er klein genug ist, um ihn in 
OCR1A zu schreiben. Meine Frequenz beträgt 14745600Hz, wenn ich sie 
durch 200000Hz teile kommt ~73 raus, wenn ich sie durch 50000 teile, 
kommt ~294 raus, da heißt ich kann nur einen Prescaler von 128 benutzen, 
oder ?

von spess53 (Gast)


Lesenswert?

Hi

>ADCW wird erstmal durch 5 geteilt, damit er klein genug ist, um ihn in
>OCR1A zu schreiben.

Wieso durch 5? Du hast eine 8 Bit PWM. Damit der ADC-Wert reinpasst must 
du durch 4 teilen. Besser noch ADLAR setzen. Dann kannst du ADCH direkt 
in OCR1AL laden.

> Meine Frequenz beträgt 14745600Hz, wenn ich sie
>durch 200000Hz teile kommt ~73 raus, wenn ich sie durch 50000 teile,
>kommt ~294 raus, da heißt ich kann nur einen Prescaler von 128 benutzen,
>oder ?

Du kannst jeden Prescaler benutzen. Mit Vorteile 128 bekommst du bei 
8-Bit-PWM ein PWM-Frequenz von 450Hz.

MfG Spess

von Thomas E. (thomase)


Lesenswert?

Da liesst du ADCW natürlich aus, ohne zu prüfen, ob er fertig ist mit 
der Wandlung.

pwm_calc_value = adc_value / 5;
Warum /5? Solche krummen Zahlen sind immer schlecht.

Fast-PWM 10 bit ist dein Freund. Da kannst du deinen ADC-Wert direkt 
reinschreiben.
Dann setzt du noch ADATE, stellst den Trigger auf Timer1-Overflow, liest 
in der Timer-Isr den ADC-Wert und schreibst ihn ins OCR-Register.
Fertig. Ein bisschen konfigurieren, eine Zeile Code(OCR1A = ADCW;).
Den Rest macht der Controller alleine und dein Programm kann Primzahlen 
berechnen.

mfg.

von Ranjid (Gast)


Lesenswert?

Thomas Eckmann schrieb:

> Fast-PWM 10 bit ist dein Freund. Da kannst du deinen ADC-Wert direkt
> reinschreiben.
> Dann setzt du noch ADATE, stellst den Trigger auf Timer1-Overflow, liest
> in der Timer-Isr den ADC-Wert und schreibst ihn ins OCR-Register.
> Fertig. Ein bisschen konfigurieren, eine Zeile Code(OCR1A = ADCW;).
> Den Rest macht der Controller alleine und dein Programm kann Primzahlen
> berechnen.
>
> mfg.

Würde auch der Timer0 oder 2 gehen, denn der timer1 (16bit) ist ja schon 
für die PWM verbraucht ?

von Ranjid (Gast)


Lesenswert?

Okay habe jetzt ADATE in TCCR1A gesetzt und in TCCR1B den Trigger auf 
Timer1 overflow aktiviert, zeigt aber keine Wirkung

von Thomas E. (thomase)


Lesenswert?

Ranjid schrieb:
> Thomas Eckmann schrieb:
>
>> Fast-PWM 10 bit ist dein Freund. Da kannst du deinen ADC-Wert direkt
>> reinschreiben.
>> Dann setzt du noch ADATE, stellst den Trigger auf Timer1-Overflow, liest
>> in der Timer-Isr den ADC-Wert und schreibst ihn ins OCR-Register.
>> Fertig. Ein bisschen konfigurieren, eine Zeile Code(OCR1A = ADCW;).
>> Den Rest macht der Controller alleine und dein Programm kann Primzahlen
>> berechnen.
>>
>> mfg.
>
> Würde auch der Timer0 oder 2 gehen, denn der timer1 (16bit) ist ja schon
> für die PWM verbraucht ?
Du kannst den Timer1 dafür nehmen. Den Overflow erzeugt er auch mit Pwm. 
Die Pwm stört das nicht. Die läuft davon unabhängig weiter.

mfg.

von Ranjid (Gast)


Lesenswert?

Hier mal mein Code mit der Timer Variante, zeigt leider keine Wirkung, 
was mache ich falsch ?
1
#define F_CPU 14745600UL
2
3
#include <avr/interrupt.h>
4
#include <avr/io.h>
5
#include <util/delay.h>
6
7
ISR(TIMER1_OVF_vect)
8
{
9
  OCR1A = ADCW;
10
}
11
12
int main(void)
13
{
14
15
  DDRB |= (1<<PB1);
16
  DDRC &= ~(1<<PC0);
17
  PORTC |= (1<<PC0);
18
  
19
  TCCR1A |= ((1<<COM1A1) | (1<<WGM10) | (1<<WGM11));  //non inverting, Prescaler 256
20
  TCCR1B |= ((1<<WGM12) | (1<<CS12));  // Prescaler 256, Fast PWM
21
  OCR1A = 40;
22
  
23
  ADMUX |= ((1<<REFS0) | (1<<REFS1));  //extern rev, adc0
24
  ADCSRA |= ((1<<ADEN) | (1<<ADIE) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2));  //adc enable, interrupt, prescaler 128, 
25
  //free running mode ADCSRB
26
  //ADCSRB |= ((1<<ADTS2) | (1<<ADTS1));
27
  
28
  sei();
29
    while(1)
30
    {
31
    //something code !?!
32
    }
33
}

von Thomas E. (thomase)


Lesenswert?

Ranjid schrieb:
> Okay habe jetzt ADATE in TCCR1A gesetzt und in TCCR1B den Trigger auf
> Timer1 overflow aktiviert, zeigt aber keine Wirkung
Huuuch?
ADATE ist nicht in TCCR1A und in TCCR1B gibt es kein Overflow.
Davon, daß der Timer zum Triggern des ADC benutzt wird, kriegt der gar 
nichts mit.
Gesetzt wird das im ADCSRA-Register(ADATE) und im ADCSRB wird die 
Trigger Source eingestellt(ADTS0 - 2).

mfg.

von Dietrich L. (dietrichl)


Lesenswert?

Ranjid schrieb:
> ADCSRA |= ((1<<ADEN) | (1<<ADIE) | (1<<ADPS0) | (1<<ADPS1) | ...
                             ^^^^
Du hast den Interrupt enabled, aber kein ISR!

Gruß Dietrich

von mysterion (Gast)


Lesenswert?

Thomas Eckmann schrieb:

> Gesetzt wird das im ADCSRA-Register(ADATE) und im ADCSRB wird die
> Trigger Source eingestellt(ADTS0 - 2).
>
> mfg.

Sry, das meinte ich, habe nur falsch geschrieben, in meinem Source den 
ich gepostet habe, hatte ich es richtig, geht aber trotzdem nicht. Werde 
mir nochmal das Dateblatt unter die Lupe nehmen.

von ADC_vect (Gast)


Lesenswert?

Statt ISR(TIMER1_OVF_vect) folgendes benutzen : ISR(ADC_vect). Wenn ich 
das Vorhaben richtig verstanden habe ( ADC messen und in OCRA laden 
sobald der Wert ermittelt ist).

von mysterion (Gast)


Lesenswert?

ADC_vect schrieb:
> Statt ISR(TIMER1_OVF_vect) folgendes benutzen : ISR(ADC_vect).
> Wenn ich
> das Vorhaben richtig verstanden habe ( ADC messen und in OCRA laden
> sobald der Wert ermittelt ist).

Habe ich auch schon versucht, jedoch bleibt die LED immer in der 
gleichen Helligkeit. Am PWM liegt es nicht, wenn ich ihn "manuel" 
verstelle, kann man sie die led steuern, der Fehler ist irgendwo beim 
ADC. Ist vielleicht meine Frequenz zu hoch ? Sie liegt nicht in dem 
optimalen Bereich von 50 und 200Khz wie Atmel empfielht. Habe leider 
keinen anderen Quarz zu hand und zum internet möchte ich ungern 
wechseln.

von Thomas E. (thomase)


Lesenswert?

1
#ifndef F_CPU
2
  #error F_CPU not defined.
3
#endif
4
5
//#include <avr/io.h>
6
#include <avr/interrupt.h>
7
/*
8
#include <stdlib.h>
9
#include <stdio.h>
10
#include <stdbool.h>
11
#include <util\delay.h>
12
#include <avr/eeprom.h>
13
#include <avr/boot.h>
14
#include <avr/pgmspace.h>
15
#include <avr/signature.h>
16
#include <avr/wdt.h>
17
*/
18
19
/*
20
#define TIMER1_MODE TIMER1_FAST_PWM_10
21
#define TIMER1_PRESCALER 64
22
#define TIMER1_OCR1A 0
23
#include "timer.h"
24
*/
25
26
void Initialize(void);
27
28
int main(void)
29
{
30
  Initialize();
31
32
  while(1)
33
  {
34
35
  }
36
}
37
38
void Initialize(void)
39
{
40
  ADMUX = (1 << REFS0) | (1 << MUX1);  //Ref = AVcc, Mux = Adc2
41
  ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADPS2)  
42
                  | (1 << ADPS1);
43
  ADCSRB = (1 << ADTS2)  | (1 << ADTS1);//
44
45
//  InitTimer();
46
47
  TCCR1A = (1 << COM1A1) | (1 << WGM11) | (1 << WGM10);
48
  TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
49
  DDRB |= (1 << 1);
50
  TIMSK1 |= (1 << TOIE1);
51
52
  sei();
53
}
54
55
ISR(TIMER1_OVF_vect)
56
{
57
  OCR1A = ADC;
58
}

Getestet mit F_CPU = 18,432 MHz und einem 10K-Poti.

Beim Timing ist zu beachten, daß der Timer den ADC nicht triggert, bevor 
die Wandlung fertig ist. Sonst fängt er mittendrin wieder von vorne an 
und liefert natürlich Unsinn.

mfg.

von Thomas E. (thomase)


Lesenswert?

ADC_vect schrieb:
> Statt ISR(TIMER1_OVF_vect) folgendes benutzen : ISR(ADC_vect). Wenn ich
> das Vorhaben richtig verstanden habe ( ADC messen und in OCRA laden
> sobald der Wert ermittelt ist).
Man kann auch den ADC-Interrupt verwenden. Aber den Timer-Interrupt 
braucht man trotzdem, da der Adc nicht durch das Overflow-Flag sondern 
durch den Aufruf des Overflow-Interrupts getriggert wird.

mfg.

von ADC_vect (Gast)


Lesenswert?

Schönen Dank, Thomas.
Die "ADC Auto Trigger Source" kannte ich bisher noch nicht.
Eine elegantere Möglichkeit wird es wohl nicht geben.

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.