Ich versuche schon seit einiger Zeit, ein Arduino dazu zu bringen, das Fernsignal meiner Klimaanlage zu replizieren, und ich gebe fast auf, nur weil diese Aufgabe so nervig ist. Das wichtigste zuerst:
Der Arduino, den ich verwende, ist der Mega 2560 , der den Mega2560 AVR-Chip verwendet , und der IR-Empfänger ist der VS1838b (logischer Pegel niedrig).
Die IR-Trägerfrequenz beträgt 38 KHz (13 uS-Timerzyklen), und die maximale Geschwindigkeit, mit der ich zuverlässige Messwerte aus dem Arduino erhalten konnte, liegt bei etwa 250 KHz (4 uS-Timerzyklen). IR-Signale werden über die Länge eines durch ein 38-kHz-Trägersignal verfälschten Signals dekodiert, aber der VS1838b gibt nur die Länge des Signals zurück (ohne den 38-kHz-Anteil).
Der Mega hat 8192 Byte Speicher, was bedeutet, dass ich sehr effizient sein muss. Seriendrucke kommen nicht in Frage, da sie dem Programm eine erhebliche Verzögerung hinzufügen, die das Lesen beeinträchtigen würde. Ich habe ein Array von Bytes verwendet und die Informationen sequentiell von links nach rechts gespeichert, aber das bedeutet, dass ich maximal 64.000 Samples haben kann.
Die IR-LED, die ich verwende, ist voll funktionsfähig, und die Lese-/Schreibgeschwindigkeit von Arduino ist ausreichend, da ich in der Lage war, eine AC-Fernbedienung zu öffnen, ein Kabel an einen der IR-Anschlüsse der Fernbedienung zu löten, es an ein Arduino und ein Relais anzuschließen das Ergebnis zu meiner IR-LED.
So wird der Timer konfiguriert:
void setup()
{
// Serial.begin (115200);
pinMode(8, INPUT);
pinMode(4, OUTPUT);
TCCR1A = B00000000;
TCCR1B = B00001001; // CTC mode with 1:1 prescaler
TIMSK1 = B00000010; // Enables Timer 1 A compare interrupt
OCR1A = 0x003E; // 3E = 4us, 7F = 8us, 9F = 10us, CF = 13us, 19F = 26us
}
Ich dachte, es wäre eine einfache Implementierung. Alles, was ich tun musste, war, das Signal genau so zu kopieren, wie es gelesen wurde (direkt von der AC-Fernbedienung), und es würde gut funktionieren (abgesehen davon, dass es bei der Speicherung ineffizient ist). Ich habe das Signal in 4-US-Zyklen kopiert, etwa so:
ISR (TIMER1_COMPA_vect)
{
temp = temp << 1;
if (PINH == 32)
temp++;
j++;
if (j == 8)
{
store[k] = temp;
k++;
j = 0;
temp = 0;
}
if (k == len)
{
k = 0;
j = 0;
flag = 1;
StopTimer();
}
}
Wobei PINH == 32 der Empfangs-Pin für die AC-Fernbedienung ist (32, weil ich direkt aus dem Register lese). Dann habe ich mit demselben Timer-Interrupt das Signal geschrieben, als es mit dem Lesen fertig war:
ISR (TIMER1_COMPA_vect)
{
if (k == len)
{
StopTimer();
CLR(5);
k = 0;
flag = 1;
}
if (bitRead(store[k], j) == 1)
{
SET(5);
}
else
{
CLR(5);
}
j--;
if (j == -1)
{
k++;
j = 7;
}
}
Aber das funktionierte nicht und ich hatte keine Ahnung warum. Ich habe dann versucht, den VS1838b zu verwenden und ihn mit einer festen PWM-Frequenz von 38 kHz über den Timer zu koppeln und den Timer abhängig von den Messwerten des Sensors zu aktivieren / deaktivieren.
Ich dachte mir, dass die PWM-Frequenz vielleicht irgendwie vom Programm beeinflusst wird, also habe ich einen anderen Arduino verwendet, um die PWM-Frequenz zu schreiben, und einen anderen, um den VS1838b zu lesen. Ich habe dann das feste 38-kHz-PWM-Schreiben an die Basis eines TIP41C-Transistors angeschlossen, den Arduino-Steuerstift an den Kollektor.
Ich bin ratlos, was möglicherweise die Ursache sein könnte, ich habe das Timer-Datenblatt doppelt überprüft, ich habe den Timer sogar manuell mit micros() -Funktionen kalibriert, aber nichts scheint zu funktionieren. Gibt es etwas, das ich grundlegend falsch mache?
Bearbeiten: Ich habe die Schaltung vereinfacht und Bilder hochgeladen , wie sie aussieht. Grundsätzlich ist D10 ein Eingang zum VS1838b-Sensor, D6 wird vom Timer verwendet, um eine PWM-Frequenz von 38 KHz zu schreiben, und D3 wird verwendet, um das NOT-Ergebnis des Sensors auszugeben. Der Code sieht so aus:
#define CLR(x) (PORTD&=(~(1<<x)))
#define SET(x) (PORTD|=(1<<x))
#define TOGGLE(x) (PORTD^=(1<<x))
void setup()
{
Serial.begin(2000000);
pinMode(10, INPUT);
pinMode(3, OUTPUT);
pinMode(6, OUTPUT);
TCCR1A = B00000000;
TCCR1B = B00001001;
TIMSK1 = B00000010;
OCR1A = 0x00CF; // 3E = 4us, 7F = 8us, 9F = 10us, CF = 13us, 19F = 26us
}
void loop()
{
if ((PINB && B00000100) == 0)
{
SET(3);
}
else
{
CLR(3);
}
}
ISR (TIMER1_COMPA_vect)
{
TOGGLE(6);
}
Und es funktioniert immer noch nicht.
IR ist so etwas wie AM-Radio, irgendwie. Sie haben ein Signal und einen Träger, aber ein Rechteckwellen-Ding. Sie und das Signal und der Träger zusammen. Um zu übertragen, müssen Sie dies rückwärts tun. Obwohl ich es an einem Punkt geschafft habe, dies mit einem PIC zu schlagen, gehen Sie davon aus, dass Sie dies nicht können, und stören Sie sich nicht. Sie müssen im Grunde einen Träger (38 kHz) erzeugen und ihn zusammen mit Ihrem Signal, ms-Pulsen oder Bruchteilen davon. Angenommen, dies ist innerhalb einer MCU nicht immer möglich, müssen Sie jede MCU bewerten, um zu sehen, ob sie dies in irgendeiner Weise tun kann.
Bei einigen verwenden Sie den Timer, um ein Signal zu erzeugen (Sie verwenden dies nicht für Interrupts zum Bitbang, Sie erzeugen das Signal selbst), und bei einigen können Sie den Ausgang basierend auf einem anderen Timer oder etwas anderem steuern. Einige STM32 haben speziell einen IR-Timer, der im Wesentlichen ein Gate zwischen zwei Timern ist.
Sie könnten es mit einem PWM schaffen, bei dem Sie manchmal ein Tastverhältnis von 50% und manchmal ein Tastverhältnis von 0% erzeugen, wobei Sie jeweils ein Timing durchführen.
Einige können Sie mit einem SPI-Controller abziehen, indem Sie den Mosi verwenden, der eine Anzahl von 0x55s und andere Werte sendet, um die richtige Anzahl von Taktzyklen und dann die richtige Anzahl von Nullen zu erzeugen, um die Lücken zu erzeugen. Dies ist wahrscheinlich der einfachere Weg, wenn die MCU ein ausreichend tiefes Fifo oder einen Weg zum DMA hat und Sie über genügend Zyklen und / oder Speicher verfügen, um das Fifo / DMA zu füttern.
Oder Sie könnten einfach ein diskretes Gate außerhalb der MCU platzieren ...
Dann speisen Sie dieses Ausgangssignal wie jede andere LED mit einem Widerstand in die IR-LED ein, um die Spannung über der LED zu begrenzen / zu steuern.
Wenn Sie kein Zielfernrohr haben, werden Sie dies äußerst schwierig finden. Möglicherweise müssen Sie alles verlangsamen, indem Sie Ihre Divisoren anpassen und es dann in ein anderes MCU- oder Himbeer-Pi- oder FPGA-Board oder etwas einspeisen, das als selbstgebauter Logikanalysator verwendet wird, um das Timing zu untersuchen. Gute Oszilloskope sind heutzutage sehr preiswert, daher lohnt es sich, einen Vierkanal zu erwerben, wenn Sie weiterhin MCU-Arbeiten durchführen möchten (dies umfasst uart, spi (zwei Datenleitungen), i2c und eine Reihe anderer Dinge) (lernen Sie, wie man liest die Protokolle, verwenden Sie nicht die Software, die versucht, sie für Sie zu lesen, sie scheitern so sehr, wie sie erfolgreich sind, und machen das Leben viel schlimmer, diese Protokolle sind einfach aus den Rohsignalen zu lesen).
Ich bin mir nicht sicher, was das Problem war, aber meine anfängliche Logik war richtig.
Um es zu beheben, habe ich Mikrocontroller vom Arduino Nano auf den neuen Arduino Nano Every getauscht, die IR-LED gegen eine neue ausgetauscht (obwohl die alte angeblich funktionierte) und den Code mit dem neuen Arduino neu geschrieben (gleiche Logik, aber mit dem neuen Atmega4809 Timer):
// Macros for easier bit manipulation
#define CLR(x) (VPORTB.OUT&=(~(1<<x)))
#define SET(x) (VPORTB.OUT|=(1<<x))
#define TOGGLE(x) (VPORTB.OUT^=(1<<x))
void setup()
{
pinMode(5, OUTPUT); // IR LED output
pinMode(9, INPUT); // VS1838b input sensor
TCA0.SINGLE.PER = 0x00D2;
TCA0.SINGLE.CMP1 = 0x00D2;
TCA0.SINGLE.INTCTRL = B00100000;
TCA0.SINGLE.CTRLA = B00000001;
}
void loop()
{
}
ISR(TCA0_CMP1_vect) {
TCA0.SINGLE.INTFLAGS |= B00100000;
if ((VPORTB.IN & B00000001) == 0)
{
TOGGLE(2);
}
else
{
CLR(2);
}
}
Das funktioniert jetzt. Vielen Dank an alle für Ihre Hilfe!
Tony Stewart EE75
Tony Stewart EE75
Alex RD
mmmm
Alex RD
Kartmann
Alex RD