Forum: Analoge Elektronik und Schaltungstechnik Atmega16 als IR-Fernbedienung


von Johannes H. (jonny089)


Angehängte Dateien:

Lesenswert?

Hallo Leute,
ich habe mit einem normalen Atmega16 eine Infrarot Fernbedienung gebaut.
Versorgt werden soll dieser mit drei Mignonzellen ohne Spannungsregler 
[4,5V] wie es in dem Artikel Versorgung aus einer Zelle beschrieben 
ist. Das Problem dabei ist, dass der normale Atmega16 mit 5V betrieben 
werden sollte, ich aber momentan keinen anderen uC zur verfügung habe.

An einer externen PWm habe ich dann eine Transistorschaltung zur 
Ansteuerung einer IR-Diode angeschlossen die ich momentan mit 50mA 
versorge. Beim Testen an dem Projektor hat die Fernbedienung nur 
manchmal funktioniert.

1) Kann das daran liegen, dass bei der Versorgung der Diode die 
Batteriespannung einbricht und dadurch dann der uC nicht mehr richtig 
arbeitet? (manchmal Funktioniert es ja)
Wäre eine Stabilisierung sinnvoll?

2) Würde es evt. reichen, Komponenten wie den ADC abzuschalten und evt. 
einen 8 MHz Oszillator zu nehmen (benutze momentan noch 16 MHz), oder 
wäre der stabile Betrieb nur mithilfe eines anderen uC's oder einer 
anderen Spannugnsversorgung realisierbar?

Mfg
Johannes

von Helmut H. (helmuth)


Lesenswert?

Probiers doch einfach mal mit stabilisierten 5V anstatt der Batterien.
Welche Werte haben Basis- und Kollektorwiderstand?
Sicher dass die IR-Signale richtig erzeugt werden?

von Roland .. (rowland)


Lesenswert?

Sehr schöner Schaltplan, ganz Analog ;-).

Ich habe auch einmal eine IR-Fernbedienung mit einem AVR (mega8) gebaut. 
Da die Reichweite gering war, habe ich den Strom durch die Diode 
deutlich erhöht, auf um die 200mA. Probleme mit Spannungseinbrüchen 
hatte ich jedoch dennoch keine und kann mir auch nicht vorstellen, dass 
bei Deinen 50mA das zum Problem werden könnte.

Bildest Du die Pulsfolge exakt nach? Also speziell die Trägerfrequenz 
sollte passen, sonst wird das Signal im Empfänger abgeschwächt.

Die Kondensatoren paralell zu den Tastern machen sich garnicht gut. 
Entprelleschaltungen sehen anders aus.

von Tim (Gast)


Lesenswert?

einfache Checkliste
1. saubere 5V Versorgung?
2. SW -> was für einen IR Code verwendest Du? Sind Codierung und 
Modulationsfreqenz richtig?

Das manchmal hört sich auch ein bisschen nach Togglebit im IR Code an - 
ist das benötigt und berücksichtigt??

Johannes Henkenjohann schrieb:
> 2) Würde es evt. reichen, Komponenten wie den ADC abzuschalten und evt.
> einen 8 MHz Oszillator zu nehmen (benutze momentan noch 16 MHz), oder
> wäre der stabile Betrieb nur mithilfe eines anderen uC's oder einer
> anderen Spannugnsversorgung realisierbar?

was hat der ADC damit zu tun?
und
fang doch erst mal mit dem internen 8MHz Takt an, das ist stabil genug. 
Richtiger IR Code vorausgesetzt spielt die 8MHz Stabilität des µC keine 
Rolle

von Johannes H. (jonny089)


Angehängte Dateien:

Lesenswert?

Hallo,
die Kondensatore parallel zu den Tastern habe ich zur Funkenlöschung 
angelötet. Entprellen mache ich nicht, da ich ja über externe Interrupts 
gehe und diese dann deaktiviere und erst nach ca 100 ms wieder freigebe.

Ich benutze die externen Interrupts, da ich später noch damit aus dem 
Power Down Mode aufwecken will. Also die Fernbedienung ist zum Ein- und 
Ausschalten eines Projektors. Verwendet wird der Sharp-Code und den habe 
ich komplett nachgebildet.

Die Trägerfrequenz ist exakt 38 kHz die ich mit Timer1 als fast PWM 
nachbilde und über einen anderen Timer dann halt je nachdem, welche Bits 
ich benötige, an PD5 ausgebe oder nicht.

Im angehängten Bild sieht man Beispielsweise ein Signal zum Einschalten 
des Projektors. Die Pulse sind immer 320 us lang und je nachdem ob es 
eine '1' oder eine '0' sein soll ist die Pause 1680 us oder 680 us lang. 
Also eine Eins hat eine "Periodendauer" von 2ms und eine Null halt von 
1ms. Das Gesamtsignal hat 15 Bits. Bestehend aus fünf Adressbits, acht 
Commandbits einem Exp Bit und einem Check Bit.

Beim Ein- und Ausschaltsignal werden diese 15 Bit im Abstand von 40ms 5 
mal wiederholt und jeweils abwechselnd nicht invertiert und invertiert 
gesendet. Hab beim uC PD5 am Oszi gemessen und das sah alles super aus, 
auch über dem Collectorwiderstand.

Ich werde die Tage jetzt einen Basiswiderstand von 750 Ohm und einen 
Collectorwiderstand von 27 Ohm einlöten, um ca. einen Diodenstrom von 
100mA zu erhalten. Dies werde ich dann mal mit einer TSOP ausmessen und 
schaun wies aussieht. Im moment sind Basiswiderstand 1,8kOhm und 
Collectorwiderstand 77 Ohm verlötet, was sicherlich nicht gut 
dimensioniert ist.

Das mit dem spannungsstabilisiertem Netzteil ist ne gute Idee. Werde ich 
austesten.

von Johannes H. (jonny089)


Lesenswert?

Achso,

Tim schrieb:
> was hat der ADC damit zu tun?

ich wollte evt. einzelne Komponenten im uC wie en ADC abschalten um die 
Leistungsaufnahme zu verringern, um die Batterie zu schonen. Deswegen 
dachte ich halt so etwas, wie beispielsweise den ADC ausschalten, weil 
er glaube ich standardgemäß immer läuft, und halt nen anderen 
Oszillator.

von Helmut H. (helmuth)


Lesenswert?

Nehme mal an du willst dieses Format: 
http://www.sbprojects.com/knowledge/ir/sharp.php

In deinem Bild sind die 12 38kHz cycles pro Bit aber nicht zu erkennen, 
kannst du mal ein Bit genauer darstellen?

Den Basiswiderstand kleiner (z.B. 470), aber die 
"Funkenlöschkondensatoren" sind (hoffentlich) nicht erforderlich und 
sollten entfernt werden.

Würde hier eher auf ein Software-Problem tippen, zeig doch mal den Code.

: Bearbeitet durch User
von Johannes H. (jonny089)


Angehängte Dateien:

Lesenswert?

Hi,
ja genau das ist der Code, nur das beim Ein- und Ausschaltsignal halt 
5*15 Bits gesendet werden (mit Originalfernbedienung gemessen). Das Bild 
oben ist die Aufnahme für einen 320us Puls der Originalfernbedienung 
mithilfe einer Photodiode.

Sind denn die einzelnen Cicles wichtig? Ich dachte es müsste nur 320us 
lang das 38kHz Signal gesendet werden. Sind ja dann auch ca 12 cicles.
Das obere bild des Gesamtsignals ist mit einer TSOP aufgenommen, daher 
sieht man die Trägerfrequenz nichtmehr.

Ich hab leider kein Bild davon, aber am PD5 habe ich mit dem Oszi genau 
die richtigen Signale gemessen. Den Code kann ich nacher mal Hochladen, 
ist aber nicht ordentlich Formatiert fürs Forum, deswegen Kommentiere 
ich den dann eben nochmal. Bin leider auch noch nicht so erfahren, was 
Mikrocontroller angeht.

Soll ich den Basiswiderstand kleiner wählen, um den Transistor voll zu 
übersteuern?

Mfg
Johannes

: Bearbeitet durch User
von Helmut H. (helmuth)


Lesenswert?

Johannes H. schrieb:
> Das obere bild des Gesamtsignals ist mit einer TSOP aufgenommen, daher
> sieht man die Trägerfrequenz nichtmehr.

Sorry, hatte ich falsch verstanden, dachte das wäre am PD5 abgenommen.

Zum Basiswiderstand siehe 
http://www.mikrocontroller.net/articles/Basiswiderstand

Bin aber kein Fachmann für das Thema, hatte bei mir diesen Wert 
verwendet.

: Bearbeitet durch User
von Johannes H. (jonny089)


Lesenswert?

Okay, das ist jetzt der Code, sieht evt. etwas unübersichtiglich aus und 
ist evt. nicht die optimale Lösung, aber soweit ich mich aufs Oszi 
verlassen kann funktioniert das.
1
/*
2
 * IR-Fernbedienung
3
 *
4
 * Created: 12.11.13
5
 * Author: Johannes H. & Markus N.
6
 */ 
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
#include <util/delay.h>
10
#include <avr/sleep.h>
11
void initPwm(void);
12
void initInt(void);
13
void initPause_680us(void);
14
void initPause_1680us(void);
15
void initPause_40ms(void);
16
void delPwm(void);
17
18
//Arrays mit den Bits zum Ein- und Ausschalten
19
int ein_5[] = {1,0,1,1,0,1,0,0,1,0,1,0,1,0,1,2,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,2,1,0,1,1,0,1,0,0,1,0,1,0,1,0,1,2,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,2,1,0,1,1,0,1,0,0,1,0,1,0,1,0,1};
20
int aus_5[] = {1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,2,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,2,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,2,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,2,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1};
21
22
volatile int pause40=0;
23
volatile int zustand=1;
24
volatile int zaehler=0;
25
volatile uint16_t tot_overflow=0;
26
int main(void)
27
{
28
  DDRD=0x20;    //PD5 auf Ausgang
29
  PORTD=0x0C;    //Interne Pullups für INT0 und INT1 aktivieren
30
  initInt();    //Interrupts aktivieren
31
  //initPwm();    
32
    while(1)
33
    {
34
    }
35
}
36
void initPwm(void)
37
{
38
  TCCR1A=(1<<WGM11)|(1<<COM1A1);      //Timer1
39
  TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS10);  //als FAST PWM
40
  ICR1=420;                //mit 38 kHz frequenz
41
  OCR1A=210;            
42
}
43
void delPwm(void)
44
{
45
  TCCR1A=0x00;
46
  TCCR1B=0x00;
47
  ICR1=0;
48
  OCR1A=0;
49
  PORTD=0x0C;
50
}
51
void initInt(void)
52
{
53
  MCUCR = 0x0A;         //INT0 und INT1 auf falling edge
54
  GICR = 0xC0;          //INT1 und INT0 freischalten        
55
  TIMSK = 0x45;         //TIMER 0/1/2 overflow interrupt enable
56
  sei();            //Interrups global freigeben
57
}
58
void initPause_680us(void)  //Timer0 680us laufen lassen
59
{
60
  TCNT0=86;
61
  TCCR0=0x03;
62
}
63
void initPause_1680us(void)  //Timer0 1680us laufen lassen
64
{
65
  TCNT0=151;
66
  TCCR0=0x04;
67
}
68
void initPause_40ms(void)  //Timer2 40ms laufen lassen
69
{
70
  TCNT2=131;
71
  TCCR2=0x07;
72
}
73
ISR (INT0_vect)        //Aus-Signal senden
74
{
75
  GICR=0x00;        //ext. Interrupts deaktivieren
76
  zaehler=0;        //Zählvariable zurücksetzen welche durch die Arrays läuft
77
  zustand=1;        //legt fest, dass das Ausschaltsignal gesendet wird
78
  initPwm();        //Started das 320us Pulspaket
79
}
80
ISR (INT1_vect)        //An-Signal senden
81
{
82
  GICR=0x00;        //ext. Interrupts deaktivieren
83
  zaehler=0;        //Zählvariable zurücksetzen
84
  zustand=2;        //An-Signal senden
85
  initPwm();        //Startet erstes Pulspaket
86
}
87
ISR (TIMER1_OVF_vect)    
88
{
89
  tot_overflow++;
90
  if(tot_overflow==12)  //320us Vorbei bei 12 Overflows
91
  {
92
    tot_overflow=0;    
93
    delPwm();      //PWM abschalten
94
    TCNT1=0;      //TImer1 zähler zurücksetzen
95
    if(zaehler==79)    //Wenn gesamtes Signal gesendet wurde ist der Zähler bei 79
96
    {
97
      zaehler=0;    
98
      zustand=3;    
99
      GICR = 0xC0;    //ext. Interrupts wieder freigeben
100
    }
101
    else
102
    {
103
      if(zustand==1)  //Aus-Signal senden
104
      {
105
        if(aus_5[zaehler]==0)
106
        {
107
          initPause_680us();
108
        }
109
        if(aus_5[zaehler]==1)
110
        {
111
          initPause_1680us();
112
        }
113
      }
114
      if(zustand==2)  //Ein-Signal senden
115
      {
116
        if(ein_5[zaehler]==0)
117
        {
118
          initPause_680us();
119
        }
120
        if(ein_5[zaehler]==1)
121
        {
122
          initPause_1680us();
123
        }
124
      }
125
      if(aus_5[zaehler]==2)
126
      {
127
        initPause_40ms();
128
      }
129
      zaehler++;
130
    }
131
  }
132
}
133
ISR (TIMER0_OVF_vect)  //Pause1 oder Pause2 vorbei
134
{
135
  TCCR0=0x00;      
136
  initPwm();      //nächstes 320us Paket senden
137
}
138
ISR (TIMER2_OVF_vect)  //8ms Overflow Vector
139
{
140
  pause40++;      
141
  TCNT2=131;
142
  if(pause40==5)    //hier sind 40ms vorbei
143
  {
144
    pause40=0;
145
    TCCR2=0x00;
146
    initPwm();    //neues Paket
147
  }
148
}





Hab auch schon überlegt, es einfach mit _delay_pause zu erledigen, da 
der Prozessor eh nichts anderes machen muss in der Zeit.

Im Prinzip arbeite ich mich mit den Timern durch das Array.

Mfg
Johannes

: Bearbeitet durch User
von Helmut H. (helmuth)


Lesenswert?

Ohne jetzt alles durchblickt zu haben: Unbedingt die Tasterabfrage ohne 
Interrupt machen. Taster prellen und es ist völlig unklar wann da was 
passiert. Das könnte das "manchmal gehts" erklären. Als nicht so 
erfahrener Programmierer ist es sicher vernünftig zunächst das delay zu 
verwenden und auf Interrupts soweit möglich zu verzichten.

Besser strukturieren:
sendeBit() triggert die 12 cycles und wartet dann die erforderliche Zeit 
je nachdem ob 1 oder 0.

sendeBefehl() ruft sendeBit für jedes zu übertragende Bit auf.

sendeEin() ruft sendeBefehl entsprechend 4 mal auf.

Und nur wenn sich alles beruhigt hat werden die Taster abgefragt und 
entsprechend sendeEin oder sendeAus aufgerufen.

Das Timing ist nicht sooo kritisch, nur die 38 KHz müssen sauber kommen.

: Bearbeitet durch User
von Johannes H. (jonny089)


Lesenswert?

Wie ich vorhin geschrieben habe möchte ich gerne sobald alles läuft den 
Controller nach jedem Senden in den power down sleep mode schicken. Da 
kann man dann super mit Int0/Int1 level interrupts aufwecken und ich 
hoffe doch dann ganz normal wieder direkt senden lassen.

Wenn ich aber im externen Interrupt direkt nach auftreten des Interrupts 
erst einmal alle externen Interrupts sperre, und die erst nach dem 
Senden des Befehls wieder freigebe, sollte doch eigentlich nichts 
unerwartetes passieren oder?
Notfalls kann ich ja auch nochmal vor der Freigabe der externen 
Interrupts die Interruptflags im GIFR Register löschen, falls noch 
irgendwie ein weiterer Interrupt gemerkt wurde.
Ich habe ja dann locker 240ms wo der Taster prellen könnte und nichts 
passieren kann. Wenn das nicht reicht, könnte ich ja nach dem Signal 
noch einmal einen Timer ein paar Millisekungen draufpacken und dann erst 
nach 500ms externe Interrupts wieder freigeben.

von Falk S. (falkschilling)


Lesenswert?

Johannes H. schrieb:
> 1) Kann das daran liegen, dass bei der Versorgung der Diode die
> Batteriespannung einbricht und dadurch dann der uC nicht mehr richtig
> arbeitet? (manchmal Funktioniert es ja)
> Wäre eine Stabilisierung sinnvoll?

Kann es sein, dass deine Taster ohne Pullup betrieben werden?

von Johannes H. (jonny089)


Lesenswert?

Doch, die sind in der Main aktiviert. Ich kann das auch leider Dienstag 
erst wieder testen. Werde erstmal die neuen Widerstände mit einem 
stabilisiertem Netzteil testen. Evt. Läufts ja dann.

von Stefan F. (Gast)


Lesenswert?

Wenn dein Problem gelöst ist, dann:

Schalte mal einen 100µF Elko zur Stabilisierung der Spannung an VCC und 
GND möglichst nahe am Emitter des Transistors und an den Vorwiderstand 
(VCC Seite) der LED. Dadurch reduzierst du ungewollte Modulation der 
Versorgungsspannung und erhöhst den Strom durch die LED.

Ich habe damit schon so mancher TV Fernbedienung doppelte Reichweite 
beigebracht.

von Falk S. (falkschilling)


Lesenswert?

Johannes H. schrieb:
> Doch, die sind in der Main aktiviert.

Ich dachte eigentlich an die externen Pullups für die Taster:
http://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen#Hardware

von Johannes H. (jonny089)


Lesenswert?

Stefan us schrieb:
> Schalte mal einen 100µF Elko zur Stabilisierung der Spannung an VCC und
> GND möglichst nahe am Emitter des Transistors und an den Vorwiderstand
> (VCC Seite) der LED. Dadurch reduzierst du ungewollte Modulation der
> Versorgungsspannung und erhöhst den Strom durch die LED.

Danke, das werde ich dann nächste Woche mal probieren.

Falk Schilling schrieb:
> Ich dachte eigentlich an die externen Pullups für die Taster:

Wäre das denn sinnvoller als die internen Pullups zu verwenden?
Ich dachte bei den AVR's macht man extra alles mit low active Tastern, 
um die zuschaltbaren internen Pullups zu verwenden.


Johannes H. schrieb:
> PORTD=0x0C;    //Interne Pullups für INT0 und INT1 aktivieren

Mit dieser Zeile aktiviere ich ja die internen Pullups für Pin 16/17, 
sprich INT0 und INT1. Und an denen hängen ja die Taster.

von Falk S. (falkschilling)


Lesenswert?

Johannes H. schrieb:
> Wäre das denn sinnvoller als die internen Pullups zu verwenden?
> Ich dachte bei den AVR's macht man extra alles mit low active Tastern,
> um die zuschaltbaren internen Pullups zu verwenden.

Sorry, mein Fehler, du hast Recht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.