Forum: Mikrocontroller und Digitale Elektronik IR Signal aufnehmen & abspielen


von TheKrokodil (Gast)


Lesenswert?

Prinzipiell will ich das IR Signal einer Fernbedienung aufnehmen und 
später wieder abspielen; damit quasi eine kleine zweite Fernbedienung 
der ersten bauen. Ich habe dazu mit einem Attiny2313 eine entsprechende 
Schaltung (IR Dioden mit Transistoren dran, TSOP1738 als Empfänger und 
einigen Tastern) aufgebaut, welche auch soweit funktioniert.

Mein Programm dazu ist aber leider scheinbar irgendwie fehlerhaft, da 
die aufgenommenen Signale nicht ganz mit den abgespielten 
übereinstimmen; und weder Receiver noch Fernseher; manchmal gerade noch 
WinLIRC das zweite Signal annehmen will.

Signal der original Fernbedienung:
1
space 1949542
2
pulse 517
3
space 2024
4
pulse 513
5
space 4552
6
pulse 513
7
space 2022
8
pulse 513
9
space 2033
10
pulse 512
11
space 4541
12
pulse 518
13
space 2027
14
pulse 514
15
space 4545
16
pulse 514
17
space 4545
18
pulse 508
19
space 2038
20
pulse 512
21
space 2023
22
pulse 530
23
space 4535
24
pulse 508
25
space 4548
26
pulse 514

Abgespieltes Duplikat:
1
space 714272
2
pulse 1185
3
space 1514
4
pulse 646
5
space 4753
6
pulse 640
7
space 1510
8
pulse 647
9
space 2056
10
pulse 621
11
space 4238
12
pulse 638
13
space 2052
14
pulse 647
15
space 4211
16
pulse 1181
17
space 4210
18
pulse 644
19
space 2051
20
pulse 652
21
space 1506
22
pulse 646
23
space 4769
24
pulse 623
25
space 4227
26
pulse 621

Selbst wenn ich mit unterschiedlichen Capture-intervallen arbeite, krieg 
ich keine passenden Ergebnisse hin; was mach ich falsch?

Quellcode:
1
/*
2
    Project I.R.
3
4
    Pinout:
5
    12 PB0 -I- IR Receiver
6
    13 PB1 -O- Programming LED
7
    14 PB2 -O- IR Emitter
8
    15 PB3 -O- Status LED
9
    16 PB4 -O- Transfer LED
10
11
    6 PD2  -I- Record Button
12
    7 PD3  -I- Playback Button
13
    8 PD4  -I- Programming Switch
14
 */
15
16
#define F_CPU 8000000L
17
18
#include <avr/io.h>
19
#include <avr/interrupt.h>
20
#include <avr/sleep.h>
21
#include <util/delay.h>
22
#include <avr/eeprom.h>
23
#include <avr/wdt.h>
24
#include <stdint.h>
25
26
//=== Variables ===
27
#define MAX_CODE 110
28
#define PLAYBACK 1
29
#define RECORDING 2
30
volatile uint8_t vMode = 0;
31
volatile uint8_t vTick= 0;
32
uint8_t ircode[MAX_CODE];
33
uint8_t ircode_len = 0;
34
uint8_t ircodeChanged = 0;
35
36
uint8_t eeProgramming EEMEM;
37
uint8_t eeIRCodeLength EEMEM;
38
uint8_t eeIRCode[MAX_CODE] EEMEM;
39
40
//=== Helper Macros ===
41
#define bit(bits) _BV(bits)
42
43
#define TOGGLE ^=
44
#define DISABLE &= ~
45
#define ENABLE |=
46
47
#define LED_STATUS (bit(PB3))
48
#define LED_XFER (bit(PB4))
49
#define LED_IR (bit(PB2))
50
#define LED_PROG (bit(PB1))
51
#define INP_T1 (bit(PD2))
52
#define INP_T2 (bit(PD3))
53
#define INP_IR (bit(PB0))
54
#define INP_PROG (bit(PD4))
55
56
#define IR ((PINB & INP_IR) == 0) //Get IR State
57
58
#define ENABLE_PWM (TCCR0A ENABLE bit(COM0A0)) //Toggle OC0A on Compare Match
59
#define DISABLE_PWM (TCCR0A DISABLE bit(COM0A0)) //Normal port operation, OC0A disconnected.
60
#define ENABLE_TIMER (TCCR1B ENABLE bit(CS10)) //1:1 ratio
61
#define DISABLE_TIMER (TCCR1B DISABLE bit(CS10)) //Deactivate Timer 1 Clocksource
62
#define ENABLE_BUTTONS (GIMSK ENABLE (bit(INT1) | bit(INT0)))
63
#define DISABLE_BUTTONS (GIMSK DISABLE (bit(INT1) | bit(INT0)))
64
65
//=== Events ===
66
//Record button
67
ISR(INT1_vect)
68
{
69
    vMode = RECORDING;
70
}
71
72
//Playback button
73
ISR(INT0_vect)
74
{
75
    vMode = PLAYBACK;
76
}
77
78
//Timer
79
ISR(TIMER1_COMPA_vect)
80
{
81
    vTick++;
82
}
83
84
//=== Methods ===
85
void IRPlayback()
86
{
87
    //Playback time!
88
    vTick = TCNT1 = 0;
89
    ENABLE_TIMER;
90
    uint8_t activeByte;
91
    uint8_t previous = 255;
92
    for (activeByte = 0; activeByte < ircode_len; activeByte++)
93
    {
94
        uint8_t content = ircode[activeByte];
95
        uint8_t activeBit;
96
97
        for (activeBit = 0; activeBit < 8; activeBit++)
98
        {
99
            //Action
100
            uint8_t state = (content >> activeBit) & 0b1;
101
102
            if (state != previous)
103
            {
104
                if (state)
105
                {
106
                    ENABLE_PWM;
107
                    PORTB ENABLE LED_XFER;
108
                }
109
                else
110
                {
111
                    DISABLE_PWM;
112
                    PORTB DISABLE LED_XFER;
113
                }
114
                previous = state;
115
            }
116
117
            //Wait
118
            while (vTick < 1) asm volatile ("NOP");
119
            vTick--;
120
        }
121
    }
122
    DISABLE_TIMER;
123
    DISABLE_PWM;
124
}
125
126
127
void NarrowIR()
128
{
129
    int8_t i;
130
    for (i = 0; i < ircode_len-1; i++)
131
    {
132
        if (ircode[i] && !ircode[i+1])
133
            ircode[i] = 0;
134
    }
135
}
136
137
void IRRecord()
138
{
139
    while (IR == 0) asm volatile ("NOP"); //wait for signal
140
    ircode_len = 0;
141
    TCNT1 = vTick = 0;
142
    ENABLE_TIMER;
143
144
    uint8_t activeByte;
145
    for (activeByte = 0; activeByte < MAX_CODE; activeByte++)
146
    {
147
        uint8_t data = 0;
148
        uint8_t activeBit;
149
        for (activeBit = 0; activeBit < 8; activeBit++)
150
        {
151
            //Write bit
152
            if (IR) {
153
                PORTB ENABLE LED_XFER;
154
                data |= (1 << activeBit);
155
                ircode_len = activeByte+1;
156
            }
157
            else
158
                PORTB DISABLE LED_XFER;
159
160
            //Wait time ..
161
            while (vTick < 1) asm volatile ("NOP");
162
            vTick--;
163
        }
164
165
        ircode[activeByte] = data;
166
    }
167
168
    ircodeChanged = 1;
169
    DISABLE_TIMER;
170
171
    //NarrowIR(); //Receiver is not that exact, I guess
172
}
173
174
inline void Reset()
175
{
176
    _delay_ms(10);
177
    cli();
178
179
    wdt_reset();
180
    wdt_enable(WDTO_500MS);
181
    while (1) asm volatile ("NOP");
182
}
183
184
void ProgrammingMode()
185
{
186
    PORTB ENABLE (LED_PROG);
187
    _delay_ms(500);
188
189
    uint8_t prog = eeprom_read_byte(&eeProgramming);
190
    while ((PIND & INP_PROG) == 0)
191
    {
192
        //Enable programming LED
193
194
195
        //Program
196
        if ((PIND & INP_T2) == 0)
197
        {
198
            prog = (prog+1) % 4;
199
            _delay_ms(300);
200
        }
201
202
        //Show
203
        if (prog & 0b01) PORTB ENABLE LED_STATUS;
204
        else PORTB DISABLE LED_STATUS;
205
        if (prog & 0b10) PORTB ENABLE LED_XFER;
206
        else PORTB DISABLE LED_XFER;
207
208
        _delay_ms(20);
209
    }
210
211
    //Programmierwert
212
    eeprom_write_byte(&eeProgramming, prog);
213
214
    //Sendecode
215
    if (ircodeChanged)
216
    {
217
        eeprom_write_byte(&eeIRCodeLength, ircode_len);
218
        eeprom_write_block(&ircode, &eeIRCode, MAX_CODE);
219
    }
220
221
    //-> Reset!
222
    Reset();
223
}
224
225
//=== PSVM ===
226
int main(void)
227
{
228
    //### Basic Port Config ###
229
    MCUSR = 0;
230
    wdt_disable();
231
232
    //Port B
233
    DDRB = LED_STATUS | LED_XFER | LED_IR | LED_PROG; //outputs
234
    PORTB = ~DDRB; //pullups for all inputs
235
236
    //Port D
237
    DDRD = 0; //outputs
238
    PORTD = ~DDRD; //pullup
239
240
    //Deactivate 1/8 prescaler
241
    CLKPR = bit(CLKPCE); // enable clock prescale change
242
    CLKPR = 0; // div1 => 8 MHz
243
244
    //Pre-INIT Loop - prevent malprogramming
245
    PORTB ENABLE (LED_PROG | LED_STATUS | LED_XFER);
246
    _delay_ms(300);
247
    PORTB DISABLE (LED_STATUS | LED_XFER | LED_PROG);
248
249
    //Sleep Mode
250
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
251
    ACSR |= bit(ACD); //disable analog comperator
252
253
    //PWM settings | Phasing: high/low = 72kHz, one phase = 36k HZ
254
    TCCR0A |= bit(WGM01); // CTC Mode: Clear timer on compare match
255
    TCCR0B |= bit(CS00); // timer source is system clock (1:1)
256
    OCR0A = (F_CPU / 72000)-1; //OCR0A = Top => 4000 kHz / 72kHz = 55
257
258
    //Timer, dynamische Frequenz (200-1000us)
259
    switch (eeprom_read_byte(&eeProgramming))
260
    {
261
        default: OCR1A = (F_CPU / 1786)-1; break; //560us, 1786hz
262
        case 1:  OCR1A = (F_CPU / 3571)-1; break; //280s, 3571hz
263
        case 2:  OCR1A = (F_CPU / 7142)-1; break; //140us, 7142hz
264
        case 3:  OCR1A = (F_CPU / 10000)-1; break; //100us, 10000hz
265
    }
266
267
    TCCR1A = 0;
268
    TCCR1B = bit(WGM12); // CTC Mode: Clear timer on compare match
269
    TIMSK |= bit(OCIE1A); // output compare interrupt enable 1a
270
271
    //Sendecode
272
    eeprom_read_block(&ircode, &eeIRCode, MAX_CODE);
273
    ircode_len = eeprom_read_byte(&eeIRCodeLength);
274
275
    //Interrupts
276
    sei(); //enable interrupts
277
278
    //Main Loop
279
    while (1) {
280
        //Go to sleep & Wait for interrupts
281
        ENABLE_BUTTONS;
282
        sleep_mode();
283
        DISABLE_BUTTONS;
284
285
        //Check for programming mode
286
        if ((PIND & INP_PROG) == 0)
287
            ProgrammingMode();
288
289
        //Something has happened!
290
        PORTB ENABLE LED_STATUS;
291
        switch (vMode)
292
        {
293
            case PLAYBACK: IRPlayback(); break;
294
            case RECORDING: IRRecord(); break;
295
        }
296
        vMode = 0;
297
        PORTB DISABLE (LED_STATUS | LED_XFER);
298
        _delay_ms(100);
299
    }
300
301
    return 0;
302
}

Jede Hilfe wäre nett ;)

von Hans (Gast)


Lesenswert?

Also jetzt reichts aber.

Warum glaubst Du steht da:

*Wichtige Regeln, erst lesen dann posten*

von Peter Z. (Gast)


Lesenswert?

Was bei soetwas sehr hilft ist ein digitales oszi!
Man kann die Signale der original Fernbedienung und der nachbau 
Fernbedienung vergleichen und dann sieht man ja wo es hackt...
So hat es zumindest bei mir recht gut funktioniert.

von TheKrokodil (Gast)


Lesenswert?

@Peter Z.: Hab ich leider nicht zur Hand; kann nur jeweils die von 
WinLIRC empfangenen Werte betrachten. Hm - kann es sein, dass der 
Empfänger selber eine Art Verzögerung hat? Aber dürfte dann auch nicht 
daran liegen, da der am PC angeschlossene quasi der selbe ist.

Wenn ich aber gezielt ein Signal sende, in dem ich die Bits selber 
direkt in die ircode[..]-Variable packe, dann stimmt es (bis auf 
Abweichungen von ~10us).

@Hans: Das Problem an den Regeln ist, dass sie relative statt absolute 
Angaben machen, und daher wieder komplett Interpretationssache sind. In 
Zeiten, in denen einzelne Klassen schon 8000 Zeilen umfassen, ist dieser 
Code mit gerade einmal 300 Zeilen noch kurz, und damit nicht "lang". 
Habe nicht beabsichtigt dich damit in diesen emotionalen Zustand zu 
versetzen.

von Peter Z. (Gast)


Lesenswert?

Weiß zwar nicht ob Dir das weiterhilft....aber ich hatte es damals 
anders gelöst.

Externer Interrupt löst bei einem "change" aus.
Bei einer fallenden Flanke wird ein Timer gestartet und misst die 
Impulsdauer.
Bei einer steigenden Flanke startet ein anderer Timer und misst die 
Impulspause.
Hatte bei mir super funktioniert.
1
// External Interrupt 0 service routine
2
interrupt [EXT_INT0] void ext_int0_isr(void)
3
{
4
delay_us(1);
5
//Bei fallender Flanke = Impulsdauer für IR 
6
if(PIND.2==0)
7
   {
8
   //Timer2 stop
9
   TIMSK &= ~(1<<TOIE2);
10
   //Timer0 starten INT0 = PIND.2
11
   TIMSK |= (1<<TOIE0);
12
   //Zähler0 zurücksetzen
13
   counter0=0x00;       
14
   //auf steigende Flanke umstellen
15
   MCUCR = 0x03;
16
   data[counterdata] = counter2;
17
   }  
18
//Bei steigender Flanke = Pausendauer für IR 
19
if(PIND.2==1)
20
   {
21
   //Timer0 stop
22
   TIMSK &= ~(1<<TOIE0);
23
   //Timer2 starten INT0 = PIND.2
24
   TIMSK |= (1<<TOIE2);
25
   //Zähler2 zurücksetzen
26
   counter2=0x00;       
27
   //auf fallende Flanke umstellen
28
   MCUCR = 0x02;
29
   //Zählerstand in Variable speichern
30
   data[counterdata] = counter0;
31
   }     
32
33
counterdata++;
34
35
}

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.