Helligkeit der LEDs mit Pulsweitenmodulation regeln
Dunkel | ![]() |
![]() |
![]() |
![]() |
---|---|---|---|---|
0 | 1 | 2 | 0 | |
33% | ![]() |
![]() |
![]() |
![]() |
0 | 1 | 2 | 0 | |
66% | ![]() |
![]() |
![]() |
![]() |
0 | 1 | 2 | 0 | |
100% | ![]() |
![]() |
![]() |
![]() |
0 | 1 | 2 | 0 |
Das Prinzip wird hier am Beispiel von 4 Helligkeitsstufen verdeutlicht. Ein Zyklus dauert 3 Takte, die LED leuchtet von 0 Takte (gar nicht) bis 3 Takte (ständig). Die LEDs sollen mit 100Hz angesteuert werden, damit kein Flackern wahrnehmbar ist. Zwei Lösungsvarianten bieten sich an:
- Realisierung per Software. Das Signal wird mittels Warteschleife oder ISR erzeugt.
- Vorteile: Beliebige PortPins als Ausgabe möglich. Keine Begrenzung durch Anzahl der PWM-Einheiten
- Nachteile: Zykluszeit kann zu lang sein. Hohe Rechenbelastung.
- Realisierung mit Hardware (Timer PWM System). Die µC habe bereits eine geeignete Hardware zur Erzeugung von PWM-Signalen eingebaut. An bestimmten Ausgängen können diese Signale abgegriffen werden.
- Vorteile: Auch kurze Zykluszeiten realisierbar, Signalerzeugung geschieht im Hintergrund, Hauptprogramm wird nicht belastet
- Nachteile: Festlegung auf bestimmte PortPins, Begrenzte Anzahl von Ausgängen
PWM per Software mit ISR
L0 des Z-Eye @1MHz soll 4 Helligkeitsstufen {0..3} annehmen können und mit einer Frequenz von 100Hz angesteuert werden. In einer CTC-ISR des 8Bit Timer0 werden die Helligkeitslevel durchgezählt. Solange der gewünschte Helligkeitslevel nicht erreicht ist, ist die LED eingeschaltet. Bei 4 Werten sind 3 ISR-Aufrufe pro Zyklus nötig, die ISR wird 300 mal pro Sekunde aufgerufen werden (300Hz). Per Tasten an T0 (runter) und T1 (hoch) soll die Helligkeit eingestellt werden.
#include <avr/io.h> // Definitionen laden #include <util/delay.h> unsigned char keyEnter; // gedrueckte Tasten void keyCheck(){ // Tastaturabfrage mit Flankendedektion static unsigned char keyOld = 0; // alter Tasten-Zustand unsigned char keyTest,tmp; keyEnter = 0; keyTest = ~PIND >> 4; if (keyOld != keyTest){ // hat sich was getan _delay_ms(10); tmp = (~PIND >> 4);// Prellen abwarten if (tmp == keyTest){ // ist es stabil? keyEnter = (~keyOld) & keyTest; // steigende Flanke keyOld = keyTest; } } } unsigned char helligkeit = 0; // Helligkeit der LED int main(){ // Hauptprogramm PORTB=0xe0; // alle LED aus DDRB= 0xff; // PORTB als Ausgang PORTD=0x7f; // PullUps an TCCR0A |= 1<<WGM01; // Timer im CTC-Mode TCCR0B |= 3; // Vorteiler 1: /1; 2: /8; 3: /64; TIMSK |= 1<<OCIE0A; // Timer/Counter0 OCMatch A I-Enable OCR0A = 51; // Wert bei dem der Timer wieder auf 0 geht sei(); // globale Interruptfreigabe while(1){ // Endlosschleife keyCheck(); // Maske,NLogik if((keyEnter&1) && helligkeit>0) helligkeit--; // T0? if((keyEnter&2) && helligkeit<3) helligkeit++; // T1? } } ISR(TIMER0_COMPA_vect){ // Interrupt Service Routine static unsigned char level = 0; // zaehlt die Hell-Level if(level>=helligkeit) PORTB&=~1;// LED0 aus else PORTB|=1; // LED0 an if (level >= 2) level = 0; else level++; }

Berechnung der ISR-Zeit
Taktzahl: 1MHz/300Hz = 3333
Vorteiler: 3333/256 = 13 -> 64 -> 3
OCR0A: 3333/64 -1 = 51
Probe: (51+1)*64 = 3328 ok
Mehr Helligkeitsstufen
L0 soll nun 16 Helligkeitsstufen {0..15} annehmen können.
Neben kleineren Änderungen muss die ISR nun mit 1500 Hz aufgerufen werden.
Modifizieren Sie die Software! Lösung anzeigen..
PWM mit Hardware
#include <avr/io.h> // Definitionen laden #include <util/delay.h> unsigned char keyEnter; //gedrueckte Tasten void keyCheck(){ // Tastaturabfrage mit Flankendedektion static unsigned char keyOld = 0; // alter Tasten-Zustand unsigned char keyTest,tmp; keyEnter = 0; keyTest = ~PIND >> 4; if (keyOld != keyTest){ // hat sich was getan _delay_ms(10); tmp = (~PIND >> 4);// Prellen abwarten if (tmp == keyTest){ // ist es stabil? keyEnter = (~keyOld) & keyTest; // steigende Flanke keyOld = keyTest; } } } unsigned char helligkeit = 0; // Helligkeit der LED int main(){ // Hauptprogramm PORTB=0xe0; // alle LED aus DDRB= 0xff; // PORTB als Ausgang PORTD=0x7f; // PullUps an TCCR0A = 1<< COM0A1 | 1 << WGM01 | 1<< WGM00; // FastPWM TCCR0B = 1; // Systemtakt durch 1 sei(); // globale Interruptfreigabe while(1){ // Endlosschleife keyCheck(); // Maske,NLogik if((keyEnter&1) && helligkeit>0) helligkeit--; // T0? if((keyEnter&2) && helligkeit<15) helligkeit++;// T1? OCR0A=helligkeit<<4; // Helligkeit*16 } }
FastPWM-Mode
Timer0 wird im FastPWM-Mode betrieben.
Er läuft von 0 bis
TOP = 0xff. Dabei wird Ausgang PB2 bei Timerwert 0 auf 1 und beim Erreichen von OC0A wieder auf 0 geschaltet.
Weil der Timer wird mit 1MHz Systemtakt betrieben wird dauert ein Zyklus 256 us.
Die Pulbreite kann mit OC0A in 256 Schritten eingestellt werden.
Die Taster verstellen den Helligkeitswert in 16er Schritten.
LED-Blöcke mit Software dimmen
#include <avr/io.h> // Definitionen laden #include <util/delay.h> #define MAX_HELL 15 // Maximale Helligkeitsstufe unsigned char keyEnter; // gedrueckte Tasten void keyCheck(){ // Tastaturabfrage mit Flankendedektion static unsigned char keyOld = 0; // alter Tasten-Zustand unsigned char keyTest,tmp; keyEnter = 0; keyTest = ~PIND >> 4; if (keyOld != keyTest){ // hat sich was getan _delay_ms(10); tmp = (~PIND >> 4);// Prellen abwarten if (tmp == keyTest){ // ist es stabil? keyEnter = (~keyOld) & keyTest; // steigende Flanke keyOld = keyTest; } } } unsigned char helligkeit[] ={0,0,0,0}; // Helligkeit der LED unsigned char auswerteTaste(){ // ermittelt die Nummer der niedrigsten Taste unsigned char i; for (i=0;i<3;i++){ if(keyEnter&(1 << i)) break; } return i; } int main(){ // Hauptprogramm PORTB=0xe0; // alle LED aus DDRB= 0xff; // PORTB als Ausgang PORTD=0x7e; // PullUps an DDRD=1; // PD0 als Ausgang zum messen TCCR0A |= 1<<WGM01; // Timer im CTC-Mode TCCR0B |= 2; // Vorteiler 1: /1; 2: /8; 3: /64; 4: /256; 5: /1024 TIMSK |= 1<<OCIE0A; // Timer/Counter0 Output Compare Match A Interrupt Enable OCR0A = 82; // Wert bei dem der Timer wieder auf 0 gesetzt wird sei(); // globale Interruptfreigabe unsigned char i,up =1; while(1){ // Endlosschleife keyCheck(); // Maske,NLogik if (keyEnter){ i = auswerteTaste(); if (helligkeit[i]>=MAX_HELL) helligkeit[i]=0; else helligkeit[i]++; } else{ if(up){ if (helligkeit[3]>=MAX_HELL) up=0; else helligkeit[3]++; } else{ if (helligkeit[3]<=0) up=1; else helligkeit[3]--; } _delay_ms(50); } } } ISR(TIMER0_COMPA_vect){ // Interrupt Service Routine static unsigned char level = 0; // zaehlt die Helligkeitslevel asm volatile ("sbi 0x12,0"); // sbi PORTD,0 = PD0 <- 1 unsigned char i,ausgabe=0; for (i=0;i<4;i++){ if(level>=helligkeit[i]) ausgabe |= 1<<i; // LEDs aus } PORTB = 0x0f | ausgabe<<4; if (level >= MAX_HELL-1) level = 0; else level++; asm volatile ("cbi 0x12,0"); // cbi PORTD,0 = PD0 <- 0 }
Die LED-Blöcke an PB7..PB4 sollen einstellbar in ihrer Helligkeit verändert werden können. Die Helligkeit von L0..L11 soll in Stufen von 0 bis 15 über die Taster T0..T2 eingestellt werden können. Der LED-Block L12..L15 gibt pulsierendes Licht ab. Die Steuerung der LED geschieht per Software in einer ISR.
- Wiederholungsrate 100 Hz
- Helligkeitstufen 0..15
- ISR soll mit 1500 Hz aufgerufen werden
- ISR-Abstand: 667 µs
- Probe: (82+1)*8 µs = 664 µs -> 1506 Hz
Aufgaben
Erstellen Sie ein Struktogramm für auswerteTaste().
Implementieren und testen Sie die Lösung.
PD0 wird beim Eintritt in die ISR eingeschaltet und beim Ende ausgeschaltet. Messen Sie die Periodendauer und die Ausführungszeit der ISR.
Die Helligkeitsabstufungen sollen nun auf 100 (0..99) erhöht werden. Modifizieren und testen Sie den Quellcode entsprechend.
Lösung anzeigen..
Der Wahrnehmung des Auges entsprechende Helligkeitsstufen
Wie bei 4.4 zu erkennen nimmt die Helligkeit bei kleinen Werten deutlich zu, bei grösseren Werten ist kaum ein Unterschied mehr zu erkennen. Das Auge hat keine lineare sondern eine logarithmische Empfindlichkeit. Die Lösung ist eine wahrnehmungsgerechte exponentielle Umrechnung der Helligkeitsstufen in Helligkeitswerte mittels Array.
- Maximale Helligkeit nun 128
- ISR-Aufruffrequenz 12,8 kHz
unsigned char hellStufe[] ={0,0,0,0}; // Helligkeitsstufe
unsigned char umrechnung[] ={0,1,2,4,8,16,32,64,128}; // Helligkeitsstufen
Fügen Sie die beiden Felder ein und modifizieren und testen Sie das Programm. Lösung anzeigen..