[C] ADC Problem + Software PWM auf Attiny13

Gast #2860912
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
}
Gast #2860984
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?
Gast #2860998
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? ;)
#2861087
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.
Gast #2861103
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
Gast #2863083
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.
Gast #2863204
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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren