Forum: Mikrocontroller und Digitale Elektronik [C] ADC Problem + Software PWM auf Attiny13


von ADC (Gast)


Lesenswert?

Hallo Leute,

ich hab folgende Hardware:
PWM Ausgang von Attiny13: PB0
1k POTI an VCC / GND, Schleifer an PB4


Nun möchte ich den Wert vom POTI per ADC abfragen und ihn prozentual als 
PWM ausgeben. Also je nach POTI Stellung den Duty Cicle der PWM ändern.
Folgender Code funktioniert leider nicht, vielleicht kann mir jemand auf 
die Sprünge helfen und ein bisschen was dazu sagen. Wäre nett.
1
/*
2
 * AVR_Dimmer.c
3
 *
4
 * Created: 30.09.2012 13:26:59
5
 *  Author: 
6
 */ 
7
8
// interner Oszillator 9,6 Mhz
9
#define F_CPU 9600000UL
10
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
14
#define PWM_Output_Port PORTB
15
#define PWM_Output_Bit 0
16
#define PWM_Output_DDR DDRB
17
18
#define POTI_Input_Port PORTB
19
#define POTI_Input_Bit 4
20
#define POTI_Input_DDR DDRB
21
22
#define set_bit(var, bit) var |= (1 << bit)
23
#define clr_bit(var, bit) var &= ~(1 << bit)
24
25
uint8_t pwm_counter = 0;
26
uint8_t volatile pwm_value;
27
uint16_t adc_in;
28
uint8_t adc_percent;
29
30
int main(void)
31
{
32
  
33
  // Initialisierung
34
  
35
  // PWM Output definieren
36
  set_bit(PWM_Output_DDR, PWM_Output_Bit);
37
  // ADC Input definieren
38
  set_bit(POTI_Input_DDR, POTI_Input_Bit);
39
  
40
  // ADC initialisieren
41
  ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);    // ADC aktivieren, Prescaler 128
42
  ADCSRB = ~(1 << ADTS2) | ~(1 << ADTS1) | ~(1 << ADTS0);  // Free Running Mode
43
  ADMUX = ~(1 << REFS0) | (1 << MUX1) | ~(1 << MUX0);  // VCC als Referenz, PB4 (ADC2) als Input  
44
    
45
  // Timer definieren
46
  TIMSK0 = (1 << TOIE0);  // Timer0 Overflow Input Enable
47
  TCCR0B = (1 << CS01);  // Prescaler 8
48
  TCNT0 = 0;        // Timer Anfangswert 0
49
  sei();          // Interrupts aktivieren
50
  
51
    while(1)
52
    {
53
    adc_in = ADCW;  // ADC auslesen
54
    adc_percent = adc_in / 1024 * 100;
55
    
56
    pwm_value = adc_percent / 100 * 255;        
57
    }
58
}
59
60
// ISR Timer0 Overflow
61
ISR(TIMER0_OVF_vect) {
62
  
63
  pwm_counter++;
64
  
65
  if (pwm_value <= pwm_counter) {
66
    set_bit(PWM_Output_Port, PWM_Output_Bit);
67
  } else {
68
    clr_bit(PWM_Output_Port, PWM_Output_Bit);  
69
  };  
70
    
71
}

von kkk (Gast)


Lesenswert?

ADC schrieb:
> ADCSRB = ~(1 << ADTS2) | ~(1 << ADTS1) | ~(1 << ADTS0);  // Free Running Mode

Also wenn Du free running nimmst, wo ist dann die passende ISR ?

von ADC (Gast)


Lesenswert?

Achso ja dann hab ich das falsch verstanden. Dann muss ich das ändern 
danke.

von ADC (Gast)


Lesenswert?

Also ich habe meinen Code nun soweit geändert:
1
/*
2
 * AVR_Dimmer.c
3
 *
4
 * Created: 30.09.2012 13:26:59
5
 *  Author: 
6
 */ 
7
8
// interner Oszillator 9,6 Mhz
9
#define F_CPU 9600000UL
10
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
14
#define PWM_Output_Port PORTB
15
#define PWM_Output_Bit 0
16
#define PWM_Output_DDR DDRB
17
18
#define POTI_Input_Port PORTB
19
#define POTI_Input_Bit 4
20
#define POTI_Input_DDR DDRB
21
22
#define set_bit(var, bit) var |= (1 << bit)
23
#define clr_bit(var, bit) var &= ~(1 << bit)
24
25
void ADC_Init();  // ADC Inititialisierung
26
uint16_t ADC_Read(uint8_t channel);    // ADC Einzelmessung
27
28
uint8_t pwm_counter = 0;
29
uint8_t volatile pwm_value;
30
uint16_t volatile adc_in;
31
uint8_t adc_percent;
32
33
int main(void)
34
{
35
  
36
  // Initialisierung
37
  
38
  // PWM Output definieren
39
  set_bit(PWM_Output_DDR, PWM_Output_Bit);
40
  // ADC Input definieren
41
  set_bit(POTI_Input_DDR, POTI_Input_Bit);
42
  
43
  // ADC initialisieren
44
  ADC_Init();
45
    
46
  // Timer definieren
47
  TIMSK0 = (1 << TOIE0);  // Timer0 Overflow Input Enable
48
  TCCR0B = (1 << CS01);  // Prescaler 8
49
  TCNT0 = 0;        // Timer Anfangswert 0
50
  sei();          // Interrupts aktivieren
51
  
52
    while(1)
53
    {
54
    adc_in = ADC_Read(2);  // ADC auslesen
55
    adc_percent = adc_in / 1024 * 100;
56
    
57
    pwm_value = adc_percent / 100 * 255;        
58
    }
59
}
60
61
// ISR Timer0 Overflow
62
ISR(TIMER0_OVF_vect) {
63
  
64
  pwm_counter++;
65
  
66
  if (pwm_value <= pwm_counter) {
67
    set_bit(PWM_Output_Port, PWM_Output_Bit);
68
  } else {
69
    clr_bit(PWM_Output_Port, PWM_Output_Bit);  
70
  };  
71
    
72
}
73
74
// ADC Initialisierung
75
void ADC_Init(void) {
76
  
77
  uint16_t result;
78
  
79
  // VCC als Referenz
80
  ADMUX = ~(1 << REFS0);
81
  
82
  // Frequenzvorverteiler 128
83
  ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
84
  
85
  // ADC aktivieren
86
  ADCSRA |= (1 << ADEN);
87
    
88
  // Dummy Readout
89
  
90
  ADCSRA |= (1 << ADSC);  // eine ADC Wandlung
91
  while (ADCSRA & (1 << ADSC)) {;};  // auf Abschluss warten
92
  result = ADCW;  // Ergebnis muss einmal gelesen werden sonst wird die nächste Wandlung nicht übernommen
93
    
94
}
95
96
// ADC Einzelmessung
97
uint16_t ADC_Read(uint8_t channel) {
98
99
  // Kanal wählen
100
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
101
  ADCSRA |= (1 << ADSC);    // eine Wandlung
102
  while (ADCSRA & (1 << ADSC)) {;};  // auf Ergebnis warten
103
  
104
  return ADCW;  // Ergebnis zurückgeben
105
  
106
}

Jedoch bekomme ich immer noch kein PWM Signal aus PB0. Sobald ich aber 
mit dem Finger an die Pins PB1 / PB2 komme, so bekomme ich ein PWM 
Signal. Folglich muss was an meiner ADC Routine falsch sein oder?

von holger (Gast)


Lesenswert?

// PWM Output definieren
  set_bit(PWM_Output_DDR, PWM_Output_Bit);
  // ADC Input definieren
  set_bit(POTI_Input_DDR, POTI_Input_Bit);

Interessant: Eingang und Ausgang werden also
mit einer Eins im DDR gesetzt? ;)

von ADC (Gast)


Lesenswert?

Oh man wie peinlich. Sowas kann auch nur mir passieren.
Leider immer noch das gleiche Phänomen

von Stefan E. (sternst)


Lesenswert?

1
  ADCSRB = ~(1 << ADTS2) | ~(1 << ADTS1) | ~(1 << ADTS0);  // Free Running Mode
2
  ADMUX = ~(1 << REFS0) | (1 << MUX1) | ~(1 << MUX0);  // VCC als Referenz, PB4 (ADC2) als Input
1
  ADMUX = ~(1 << REFS0);
Da ist offenbar Nachholbedarf für Bit-Arithmetik, denn keine dieser 
Zeilen macht das, was von dir beabsichtigt ist. Die ADMUX-Zeile z.B. 
setzt das ADLAR-Bit.

1
    adc_percent = adc_in / 1024 * 100;
2
    
3
    pwm_value = adc_percent / 100 * 255;
Und auch bei der Integer-Arithmetik hapert es. Dass zur Zeit überhaupt 
irgendwelche unterschiedlichen Werte in pwm_value stehen, liegt an der 
fehlerhaften ADMUX-Zeile. Wenn die erst mal korrigiert ist, steht da 
immer nur 0 drin.

von ADC (Gast)


Lesenswert?

Stefan Ernst schrieb:
> adc_percent = adc_in / 1024 * 100;
>
>     pwm_value = adc_percent / 100 * 255;Und auch bei der Integer-Arithmetik 
hapert es. Dass zur Zeit überhaupt
> irgendwelche unterschiedlichen Werte in pwm_value stehen, liegt an der
> fehlerhaften ADMUX-Zeile. Wenn die erst mal korrigiert ist, steht da
> immer nur 0 drin.

Das kommt davon wenn man noch auf dem PC denkt, wo Floatberechnungen ja 
kein Problem sind. facepalm

von ADC (Gast)


Lesenswert?

Also egal ob ich
1
ADMUX = 0x00
 oder
1
ADMUX &= ~(1 << REFS0)
 schreibe, ich habe immer noch den gleichen Effekt, dass sich die PWM 
nur dann ändert, wenn ich mit dem Finger auf die Pins PB1 / PB2 vom 
Tiny13 komme.

von ADC (Gast)


Lesenswert?

Ok, das Problem mit dem Finger und dem Wert habe ich nun behoben. Mal 
wieder ein peinlicher Fehler den man selber nicht sieht. VCC vom Attiny 
nicht angeschlossen.

Jedoch gibt mein ADC immer noch nix zurück jmd eine Idee?

von ADC (Gast)


Lesenswert?

Mit Hardware PWM funktioniert nun alles, auch der ADC

von Borsty B. (mantabernd)


Lesenswert?

Hi,

mich würde interessieren wie die Umrechung in Prozent denn nun aussieht?

Würdest du den kompletten, funktinoierenden Code nochmal posten?
Danke!

Gruß

von ADC (Gast)


Lesenswert?

Ich habe einen viel einfacheren Weg gewählt.

Ein 10bit Wert geht ja von 0 - 1023 und ein 8bit Wert von 0 - 255. Das 
einfachste zum umwandeln ist dann einfach durch 4 zu teilen.

von Unwissender (Gast)


Lesenswert?

ADLAR = 1
Consequently, if
the result is left adjusted and no more than 8-bit precision is 
required, it is sufficient to read ADCH.

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.