Soft-PWM, GCC, ATMEGA16

Gast #1746580
Lesenswert?

Guten Abend,

ich versuche schon den ganzen Tage ein kleines Programm zu schreiben, 
welches im Endeffekt eine LED dimmen soll.
Leider klappt es einfach nicht :-(.

Da ich schon in vielen Tutorials gesucht habe (auch im GCC-Tutorial von 
mikrocontroller.net), jedoch meinen Fehler nicht finden kann, wende ich 
mich nun an euch.

Die verbaute Hardware:
- der Atmega 16
- eine rote LED an PA2 und
- ein Taster an PB2

Mein Code:
1
#ifndef F_CPU
2
#define F_CPU 1000000UL 
3
#endif
4

5
#include <avr/io.h>
6
#include <util/delay.h>
7
#include <avr/interrupt.h>
8

9
#define Rot_an    PORTA |= (1 << PA2)
10
#define Rot_aus    PORTA &= ~(1 << PA2)
11

12
// Variablen definieren
13
uint8_t timer;
14
uint8_t rot;
15

16
// Interrupt vom Taster 
17
ISR(INT2_vect)
18
{ 
19
rot = rot + 10;
20
// warte und lösche Interruptflag von Int2 um Tastenprellen zu entgehen
21
_delay_ms(50);
22
GIFR = (1<<INTF2); 
23
}
24

25
// Timer-Overflow-Interrupt
26
ISR (TIMER0_OVF_vect)
27
{ 
28
// zähle die Variable Timer von 0-255
29
timer++;
30
if (timer > 255) {timer = 0;}
31
}
32

33
int main(void){
34

35
// Variablen definieren
36
timer=0;
37
rot=0;
38

39
// PortA zu Ausgängen
40
DDRA= 0xff; 
41
PORTA= 0b00000000; 
42

43
// PB0 als Eingang, Pull-Up aktivieren
44
DDRB  &= ~(1<<PB2);  
45
PORTB |= (1<<PB2);     
46

47
// Int2 aktivieren
48
GICR|=(1<<INT2);     
49
//Low Level erzeugt  Interrupt            
50
MCUCR &= ~(1<<ISC01)|(1<<ISC00);   
51
                  
52
// Timer 0 konfigurieren: Prescaler aus, FastPWM-Modus an
53
  TCCR0 = (1<<CS00);
54

55
// Overflow Interrupt aktivieren
56
    TIMSK |= (1<<TOIE0);
57

58
// Global Interrupts aktivieren
59
   sei();
60

61

62
while(1){
63

64
if (timer < rot) {Rot_an;}
65
else {Rot_aus;}
66

67
} //eof while
68

69
} //eof main

Die Interrupt vom Taster funktioniert einwandfrei(auch wenn ich in 
dieser 50ms Zeit verschwende, was ja nicht elegant ist, aber zu 
Testzwecken ausreicht denke ich).

Ich habe den Code außerdem im AVR-Studio simuliert. Hier zählt er die 
Variable "timer" immer von 0-255 hoch, vergleicht diese aber nie mit der 
Variable rot.

Vielen Dank schonmal für eure Hilfe !
Gast #1746603
Lesenswert?

Wohl der Standart-Fehler:

Die beiden Variablen
1
uint8_t timer;
2
uint8_t rot;
sollten als volatile deklariert werden.

Der Compiler sieht folgendes:
1
int main(void){
2
  // Variablen definieren
3
  timer=0;
4
  rot=0;
5
  [...]//Initialisierungen
6
  while(1){
7
    if (timer < rot) {Rot_an;}
8
    else {Rot_aus;}
9
  } //eof while
10
} //eof main
Er weiß nicht, dass timer und rot noch von einer Interrupt-Funktion, 
die er im main() nicht findet, verändert werden können. Das teilst Du 
ihm mit volatile mit.
1
volatile uint8_t timer;
2
volatile uint8_t rot;
Gast #1746611
Lesenswert?

Ergänzung:
Der Compiler bemerkt, dass er die if-Bedingung super optimieren kann. 
Beide Variablen sind aus seiner Sicht konstant =0, da muss er nicht mehr 
viel vergleichen sondern kann direkt den else-Zweig ausführen.

Durch volatile teilst Du dem Compiler mit, dass er  sich nicht darauf 
verlassen sollte, dass die Variable konstant bleibt.
Gast #1751240
Lesenswert?

und noch ein tip:

in deiner Timer ISR:

if (timer > 255) {timer = 0;}

kannst Du Dir sparen. "timer" ist eine 8Bit Variable und läuft 
automatisch bei 255+1 auf 0 über. Vorausgesetzt, dass der Timer0 bei 
deinem Atmel ein 8Bit Timer ist kannst Du auch direkt das Register TCNT0 
nehmen und dir die Variable "timer" sparen.

ciao

Christian

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