Hallo
Weil ich ja schon immer mal etwas mit einer PWM-Ansteuerung spielen wollte, habe ich mal Grundlagenforschung betrieben. Ich denke, obwohl das folgende noch Betastatus ist, hat es sich gelohnt. Meine bee kann jetzt ein pulsweitenmoduliertes 36kHz-IR-Signal erzeugen und es mit den LineLEDs abstrahlen.
Basis ist der Timer2 der im Phase-Correct-PWM-Mode betrieben wird. Das hört sich zwar übelst kompliziert an, ist aber eigentlich ganz einfach;) Anders als in den "normalen" Modi zählt hier der Counter von null bis 255 und dann wieder zurück nach null. Wenn er beim Runterzählen bei null angekommen ist, wird der Overflow-Interrupt ausgelöst und der Zyklus beginnt erneut. Deshalb dauert in dieser Betriebsart ein Zyklus 512 Countertakte (Das genaue Timing muss ich noch überprüfen)
Um nun auf die 36kHz zu kommen, benötigt man bei der bee 15MHz/36kHz=416,irgendwas Takte. Deshalb verwende ich den selben Trick wie Waste beim asuro und lade das Zählregister in der Overflow-ISR mit einem neuen Startwert ab dem der Counter dann weiter bis 255 und wieder zurück bis null zählt. Den Wert errechnet man so: 512-416=96. Zusätzlich benötigt man noch 3 Takte für das Laden des Wertes in das Zählregister, damit sind es 99 die in der Overflow-ISR in das Zählregister geladen werden um den 36kHz-Takt zu erzeugen.
Um nun die IR-LEDs mit dieser Frequenz anzusteuern verwendet man das OCR-Register. Jedesmal, wenn der Zählwert so groß wie der Wert im OCR ist, wird ein CompareMatch-Interrupt ausgelöst. Und zwar jedesmal, wenn die Werte gleich sind, also auch beim Runterzählen. Wenn beim Start des Timers die LED aus ist, wird sie bei OCR eingeschaltet, bleibt an während der Zähler 255 erreicht und brennt dann weiter, bis der Zähler beim Runterzählen wieder OCR erreicht. Dann wird die LED wieder getoggelt, also ausgeschaltet und bleibt solange aus, bis der Zähler nach Erreichen von null und Laden von Startwert in der OVF-ISR den OCR-Wert erneut erreicht. An diesem Punkt ist dann eine Periode beendet.
Der Wert fürs OCR-Register bestimmt die Hell- und Dunkelanteile einer Periode des Signals. Um 50:50 zu erhalten, muss die LED ca. 208 Takte eingeschaltet und 208 Takte ausgeschaltet sein, also 104 Takte vor 255 einschalten beim Hochzählen und beim Runterzählen auch nach 104 Takten. Das ergibt als Wert für das OCR2-Register 255-104=151. Alle Werte die größer sind, verringern den Leuchtanteil der IR-LEDs, Werte kleiner 100 sind nicht erlaubt. Ebenso funktioniert es bei Werten über 253 nicht mehr.
Dieses kleine Demo zeigt wie es gemacht wird und wie es funktioniert:
Code:
// Nibobee 36kHz mit Timer2 in PhaseCorrect-PWM-Mode. 12.1.2010 mic
#define LineLEDs_on PORTB &=~(1<<PB4)
#define LineLEDs_off PORTB |= (1<<PB4)
#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/sens.h>
volatile uint8_t count36kHz;
void Sleep(uint8_t pause);
void Msleep(uint16_t pause);
int main(void)
{
led_init();
sens_init();
// Setup Timer2
TCCR2 = (1 << WGM20)|(1 << CS20); // PhaseCorrect-PWM, no prescaling, no OC2-Pin!
TCNT2 = 96; // (512-416) 36kHz @15MHz
OCR2 = 151; // (255-(208/2)) 151 ist 50:50 Compare Match für symetrische Halbwellen
TIMSK |= (1 << OCIE2)|(1 << TOIE2); // Comp und OVF-ISR enable, Overflow bei Bottom!
enable_interrupts();
Msleep(2000); // wait4programmer
led_set(0,1);
DDRB |= (1<<PB4); // LineLED ist ein Ausgang
LineLEDs_on; // LineLED schaltet gegen GND!
while(!sens_getLeft() & !sens_getRight()) // solange keine Taste gedrückt wird
{Msleep(200); PORTB ^= (1<<PB0);} // hektisches Blinken mit LED0
while(1)
{
if(sens_getLeft() == -1) {OCR2=200; PORTB &= ~15; led_set(0,1);}
if(sens_getLeft() == 1) {OCR2=230; PORTB &= ~15; led_set(1,1);}
if(sens_getRight() == 1) {OCR2=250; PORTB &= ~15; led_set(2,1);}
if(sens_getRight() == -1){OCR2=253; PORTB &= ~15; led_set(3,1);} // minwert!
Msleep(100);
}
return(0);
}
ISR (TIMER2_COMP_vect)
{
PORTB ^= (1<<PB4); // LineLEDs togglen
}
// Frequenzkorrektur 512-416 plus 3 Takte fürs Laden von TCNT2?
ISR (TIMER2_OVF_vect)
{
TCNT2 = 99;
if(count36kHz) count36kHz--;
}
void Sleep(uint8_t pause) // 1/36000 Pause blockierend
{
count36kHz=pause;
while(count36kHz);
}
void Msleep(uint16_t pause) // 1/1000 Pause blockierend
{
while(pause--) Sleep(36);
}
Zusätzlich ist noch die Sleep()-Funktion eingebaut für die asuro-Umsteiger;) Sie ersetzt hier die delay()-Funktion der nibobee-Lib. Als IR-Detektor dient mein RP6:
Code:
// Status des TSOP an LEDs anzeigen 12.1.2010 mic
// Funktion kann einfach mit einer Fernbedienung getestet werden
#include "RP6RobotBaseLib.h"
int main(void)
{
initRobotBase();
powerON();
while(1)
{
if(PINB & ACS) setLEDs(1); else setLEDs(63);
}
return(0); // wird nie erreicht
}
Das Video ist dazu ist äzend, ich zeige es nur weil es eben existiert:
Bild hier
http://www.youtube.com/watch?v=Lrj1Xzp791c
Möglicherweise stimmt das Timing nicht 100%ig. Da ich kein Oszi habe, kann ich das Signal nicht genau ausmessen. Vermutlich passt es nicht Taktgenau (512, 104, 151, 99?), aber es ist offensichtlich innerhalb der TSOP-Toleranz. Grundsätzlich funktioniert das wohl auch mit dem SFH.
Gruß
mic
Lesezeichen