www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik IR Signal aufnehmen & abspielen


Autor: TheKrokodil (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
space 1949542
pulse 517
space 2024
pulse 513
space 4552
pulse 513
space 2022
pulse 513
space 2033
pulse 512
space 4541
pulse 518
space 2027
pulse 514
space 4545
pulse 514
space 4545
pulse 508
space 2038
pulse 512
space 2023
pulse 530
space 4535
pulse 508
space 4548
pulse 514

Abgespieltes Duplikat:
space 714272
pulse 1185
space 1514
pulse 646
space 4753
pulse 640
space 1510
pulse 647
space 2056
pulse 621
space 4238
pulse 638
space 2052
pulse 647
space 4211
pulse 1181
space 4210
pulse 644
space 2051
pulse 652
space 1506
pulse 646
space 4769
pulse 623
space 4227
pulse 621

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

Quellcode:
/*
    Project I.R.

    Pinout:
    12 PB0 -I- IR Receiver
    13 PB1 -O- Programming LED
    14 PB2 -O- IR Emitter
    15 PB3 -O- Status LED
    16 PB4 -O- Transfer LED

    6 PD2  -I- Record Button
    7 PD3  -I- Playback Button
    8 PD4  -I- Programming Switch
 */

#define F_CPU 8000000L

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>
#include <stdint.h>

//=== Variables ===
#define MAX_CODE 110
#define PLAYBACK 1
#define RECORDING 2
volatile uint8_t vMode = 0;
volatile uint8_t vTick= 0;
uint8_t ircode[MAX_CODE];
uint8_t ircode_len = 0;
uint8_t ircodeChanged = 0;

uint8_t eeProgramming EEMEM;
uint8_t eeIRCodeLength EEMEM;
uint8_t eeIRCode[MAX_CODE] EEMEM;

//=== Helper Macros ===
#define bit(bits) _BV(bits)

#define TOGGLE ^=
#define DISABLE &= ~
#define ENABLE |=

#define LED_STATUS (bit(PB3))
#define LED_XFER (bit(PB4))
#define LED_IR (bit(PB2))
#define LED_PROG (bit(PB1))
#define INP_T1 (bit(PD2))
#define INP_T2 (bit(PD3))
#define INP_IR (bit(PB0))
#define INP_PROG (bit(PD4))

#define IR ((PINB & INP_IR) == 0) //Get IR State

#define ENABLE_PWM (TCCR0A ENABLE bit(COM0A0)) //Toggle OC0A on Compare Match
#define DISABLE_PWM (TCCR0A DISABLE bit(COM0A0)) //Normal port operation, OC0A disconnected.
#define ENABLE_TIMER (TCCR1B ENABLE bit(CS10)) //1:1 ratio
#define DISABLE_TIMER (TCCR1B DISABLE bit(CS10)) //Deactivate Timer 1 Clocksource
#define ENABLE_BUTTONS (GIMSK ENABLE (bit(INT1) | bit(INT0)))
#define DISABLE_BUTTONS (GIMSK DISABLE (bit(INT1) | bit(INT0)))

//=== Events ===
//Record button
ISR(INT1_vect)
{
    vMode = RECORDING;
}

//Playback button
ISR(INT0_vect)
{
    vMode = PLAYBACK;
}

//Timer
ISR(TIMER1_COMPA_vect)
{
    vTick++;
}

//=== Methods ===
void IRPlayback()
{
    //Playback time!
    vTick = TCNT1 = 0;
    ENABLE_TIMER;
    uint8_t activeByte;
    uint8_t previous = 255;
    for (activeByte = 0; activeByte < ircode_len; activeByte++)
    {
        uint8_t content = ircode[activeByte];
        uint8_t activeBit;

        for (activeBit = 0; activeBit < 8; activeBit++)
        {
            //Action
            uint8_t state = (content >> activeBit) & 0b1;

            if (state != previous)
            {
                if (state)
                {
                    ENABLE_PWM;
                    PORTB ENABLE LED_XFER;
                }
                else
                {
                    DISABLE_PWM;
                    PORTB DISABLE LED_XFER;
                }
                previous = state;
            }

            //Wait
            while (vTick < 1) asm volatile ("NOP");
            vTick--;
        }
    }
    DISABLE_TIMER;
    DISABLE_PWM;
}


void NarrowIR()
{
    int8_t i;
    for (i = 0; i < ircode_len-1; i++)
    {
        if (ircode[i] && !ircode[i+1])
            ircode[i] = 0;
    }
}

void IRRecord()
{
    while (IR == 0) asm volatile ("NOP"); //wait for signal
    ircode_len = 0;
    TCNT1 = vTick = 0;
    ENABLE_TIMER;

    uint8_t activeByte;
    for (activeByte = 0; activeByte < MAX_CODE; activeByte++)
    {
        uint8_t data = 0;
        uint8_t activeBit;
        for (activeBit = 0; activeBit < 8; activeBit++)
        {
            //Write bit
            if (IR) {
                PORTB ENABLE LED_XFER;
                data |= (1 << activeBit);
                ircode_len = activeByte+1;
            }
            else
                PORTB DISABLE LED_XFER;

            //Wait time ..
            while (vTick < 1) asm volatile ("NOP");
            vTick--;
        }

        ircode[activeByte] = data;
    }

    ircodeChanged = 1;
    DISABLE_TIMER;

    //NarrowIR(); //Receiver is not that exact, I guess
}

inline void Reset()
{
    _delay_ms(10);
    cli();

    wdt_reset();
    wdt_enable(WDTO_500MS);
    while (1) asm volatile ("NOP");
}

void ProgrammingMode()
{
    PORTB ENABLE (LED_PROG);
    _delay_ms(500);

    uint8_t prog = eeprom_read_byte(&eeProgramming);
    while ((PIND & INP_PROG) == 0)
    {
        //Enable programming LED


        //Program
        if ((PIND & INP_T2) == 0)
        {
            prog = (prog+1) % 4;
            _delay_ms(300);
        }

        //Show
        if (prog & 0b01) PORTB ENABLE LED_STATUS;
        else PORTB DISABLE LED_STATUS;
        if (prog & 0b10) PORTB ENABLE LED_XFER;
        else PORTB DISABLE LED_XFER;

        _delay_ms(20);
    }

    //Programmierwert
    eeprom_write_byte(&eeProgramming, prog);

    //Sendecode
    if (ircodeChanged)
    {
        eeprom_write_byte(&eeIRCodeLength, ircode_len);
        eeprom_write_block(&ircode, &eeIRCode, MAX_CODE);
    }

    //-> Reset!
    Reset();
}

//=== PSVM ===
int main(void)
{
    //### Basic Port Config ###
    MCUSR = 0;
    wdt_disable();

    //Port B
    DDRB = LED_STATUS | LED_XFER | LED_IR | LED_PROG; //outputs
    PORTB = ~DDRB; //pullups for all inputs

    //Port D
    DDRD = 0; //outputs
    PORTD = ~DDRD; //pullup

    //Deactivate 1/8 prescaler
    CLKPR = bit(CLKPCE); // enable clock prescale change
    CLKPR = 0; // div1 => 8 MHz

    //Pre-INIT Loop - prevent malprogramming
    PORTB ENABLE (LED_PROG | LED_STATUS | LED_XFER);
    _delay_ms(300);
    PORTB DISABLE (LED_STATUS | LED_XFER | LED_PROG);

    //Sleep Mode
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    ACSR |= bit(ACD); //disable analog comperator

    //PWM settings | Phasing: high/low = 72kHz, one phase = 36k HZ
    TCCR0A |= bit(WGM01); // CTC Mode: Clear timer on compare match
    TCCR0B |= bit(CS00); // timer source is system clock (1:1)
    OCR0A = (F_CPU / 72000)-1; //OCR0A = Top => 4000 kHz / 72kHz = 55

    //Timer, dynamische Frequenz (200-1000us)
    switch (eeprom_read_byte(&eeProgramming))
    {
        default: OCR1A = (F_CPU / 1786)-1; break; //560us, 1786hz
        case 1:  OCR1A = (F_CPU / 3571)-1; break; //280s, 3571hz
        case 2:  OCR1A = (F_CPU / 7142)-1; break; //140us, 7142hz
        case 3:  OCR1A = (F_CPU / 10000)-1; break; //100us, 10000hz
    }

    TCCR1A = 0;
    TCCR1B = bit(WGM12); // CTC Mode: Clear timer on compare match
    TIMSK |= bit(OCIE1A); // output compare interrupt enable 1a

    //Sendecode
    eeprom_read_block(&ircode, &eeIRCode, MAX_CODE);
    ircode_len = eeprom_read_byte(&eeIRCodeLength);

    //Interrupts
    sei(); //enable interrupts

    //Main Loop
    while (1) {
        //Go to sleep & Wait for interrupts
        ENABLE_BUTTONS;
        sleep_mode();
        DISABLE_BUTTONS;

        //Check for programming mode
        if ((PIND & INP_PROG) == 0)
            ProgrammingMode();

        //Something has happened!
        PORTB ENABLE LED_STATUS;
        switch (vMode)
        {
            case PLAYBACK: IRPlayback(); break;
            case RECORDING: IRRecord(); break;
        }
        vMode = 0;
        PORTB DISABLE (LED_STATUS | LED_XFER);
        _delay_ms(100);
    }

    return 0;
}

Jede Hilfe wäre nett ;)

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also jetzt reichts aber.

Warum glaubst Du steht da:

*Wichtige Regeln, erst lesen dann posten*

Autor: Peter Z. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: TheKrokodil (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Z. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
delay_us(1);
//Bei fallender Flanke = Impulsdauer für IR 
if(PIND.2==0)
   {
   //Timer2 stop
   TIMSK &= ~(1<<TOIE2);
   //Timer0 starten INT0 = PIND.2
   TIMSK |= (1<<TOIE0);
   //Zähler0 zurücksetzen
   counter0=0x00;       
   //auf steigende Flanke umstellen
   MCUCR = 0x03;
   data[counterdata] = counter2;
   }  
//Bei steigender Flanke = Pausendauer für IR 
if(PIND.2==1)
   {
   //Timer0 stop
   TIMSK &= ~(1<<TOIE0);
   //Timer2 starten INT0 = PIND.2
   TIMSK |= (1<<TOIE2);
   //Zähler2 zurücksetzen
   counter2=0x00;       
   //auf fallende Flanke umstellen
   MCUCR = 0x02;
   //Zählerstand in Variable speichern
   data[counterdata] = counter0;
   }     

counterdata++;

}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.